mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Merge] (GLImpl, DirectGLES, DirectVulkan): complete direct state access and the compressed-texture family
This commit is contained in:
@@ -1047,9 +1047,9 @@ DECLARE_GL_FUNCTION_HEAD(void, TextureStorage3DMultisample, GLuint texture, GLsi
|
||||
DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage1D, texture, level, xoffset, x, y, width)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height)
|
||||
@@ -1835,9 +1835,9 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBooleanIndexedvEXT, GLenum target, GLuint
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureImage3DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureImage3DEXT, texture, target, level, internalformat, width, height, depth, border, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureImage2DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureImage2DEXT, texture, target, level, internalformat, width, height, border, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureImage1DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureImage1DEXT, texture, target, level, internalformat, width, border, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage3DEXT, texture, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage3DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1DEXT, texture, target, level, xoffset, width, format, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, CompressedTextureSubImage1DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureImageEXT, GLuint texture, GLenum target, GLint lod, void* img) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureImageEXT, texture, target, lod, img)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedMultiTexImage3DEXT, GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedMultiTexImage3DEXT, texunit, target, level, internalformat, width, height, depth, border, imageSize, bits)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedMultiTexImage2DEXT, GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* bits) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedMultiTexImage2DEXT, texunit, target, level, internalformat, width, height, border, imageSize, bits)
|
||||
|
||||
@@ -4099,11 +4099,171 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
"1D textures are not supported by this implementation"));
|
||||
}
|
||||
|
||||
// The three-dimensional twin of CompressedTexSubImage2D_State: a block-aligned box of the
|
||||
// compressed image the level shadows is replaced, slice by slice. Same deviation as the 2D form
|
||||
// - the uncompressed texel shadow beside it is NOT touched, so what changes is the image
|
||||
// glGetCompressedTexImage hands back, not what the level samples as.
|
||||
void CompressedTexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
||||
const void* data) {
|
||||
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||
// ======================= Converting ================================
|
||||
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
// Zero block width doubles as "format is not a specific compressed format", the
|
||||
// INVALID_ENUM case - one lookup answers both questions.
|
||||
const auto compressedInfo = MG_Util::GetCompressedFormatInfo(format);
|
||||
|
||||
// ===================== Error Checking ==============================
|
||||
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
||||
// A proxy holds no image to modify; only the glTexImage*/glCompressedTexImage* pair
|
||||
// accepts one.
|
||||
if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"A proxy target has no texture image to modify."));
|
||||
return;
|
||||
}
|
||||
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
||||
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
||||
if (width < 0 || height < 0 || depth < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"width, height and depth must be non-negative."));
|
||||
return;
|
||||
}
|
||||
if (compressedInfo.blockWidth == 0) {
|
||||
RecordUnsupportedCompressedFormat(__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
auto* textureMipmapObject = MG_State::GLState::AsMipmapTexture(textureObject.get());
|
||||
if (textureMipmapObject == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
||||
return;
|
||||
}
|
||||
// GL 4.6 core 8.7: INVALID_OPERATION unless the image being modified is stored in
|
||||
// exactly this compressed format. That is also what makes the block arithmetic below
|
||||
// sound - the level's grid is measured with THIS format's block size.
|
||||
const GLenum levelFormat =
|
||||
textureMipmapObject->GetMipmapCompressedFormat(textureUploadTarget, static_cast<Uint>(level));
|
||||
if (levelFormat != format) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"format does not match the internal format of the texture image."));
|
||||
return;
|
||||
}
|
||||
|
||||
const IntVec3 levelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, static_cast<Uint>(level));
|
||||
const Int levelDepth = std::max(levelSize.z(), 1);
|
||||
// Subtractions rather than sums for the reason CompressedTexSubImage2D_State spells out:
|
||||
// offset + extent are both application-supplied GLints and a signed overflow is undefined.
|
||||
if (xoffset < 0 || yoffset < 0 || zoffset < 0 || width > levelSize.x() - xoffset ||
|
||||
height > levelSize.y() - yoffset || depth > levelDepth - zoffset) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"The replaced region does not lie within the texture image."));
|
||||
return;
|
||||
}
|
||||
// GL 4.6 core 8.7 for block-based formats: the region must start on a block boundary
|
||||
// and must either be a whole number of blocks wide/high or run to the image's edge. Every
|
||||
// format that reaches here is 4x4x1, so the depth axis carries no block alignment rule -
|
||||
// each slice is its own block grid.
|
||||
const Int blockWidth = static_cast<Int>(compressedInfo.blockWidth);
|
||||
const Int blockHeight = static_cast<Int>(compressedInfo.blockHeight);
|
||||
const Bool alignedX = (xoffset % blockWidth == 0) &&
|
||||
(width % blockWidth == 0 || xoffset + width == levelSize.x());
|
||||
const Bool alignedY = (yoffset % blockHeight == 0) &&
|
||||
(height % blockHeight == 0 || yoffset + height == levelSize.y());
|
||||
if (!alignedX || !alignedY) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"The replaced region is not aligned to the format's compressed blocks."));
|
||||
return;
|
||||
}
|
||||
// Exactly the size the format and dimensions imply, which is also what keeps the copy
|
||||
// below in bounds.
|
||||
const SizeT expectedImageSize =
|
||||
MG_Util::CalculateCompressedTextureImageSize(compressedInfo, {width, height, depth});
|
||||
if (imageSize < 0 || static_cast<SizeT>(imageSize) != expectedImageSize) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"imageSize does not match the compressed image size."));
|
||||
return;
|
||||
}
|
||||
|
||||
// ======================= Processing ================================
|
||||
if (!ValidateCompressedUnpackBufferSource(data, expectedImageSize, __func__)) return;
|
||||
const void* compressedBytes = CompressedUnpackSource(data);
|
||||
if (expectedImageSize == 0) return; // a zero-sized region is a legal no-op
|
||||
if (compressedBytes == nullptr) {
|
||||
// No unpack buffer and a null client pointer: there is nothing to read. GL leaves
|
||||
// this undefined rather than erroring, and dereferencing it is the one answer that
|
||||
// is never acceptable.
|
||||
MGLOG_D("%s: null data with no pixel unpack buffer bound, nothing to replace", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
static std::atomic<Bool> announcedNoCodec3D{false};
|
||||
if (!announcedNoCodec3D.exchange(true)) {
|
||||
MGLOG_W("%s: the compressed blocks are stored verbatim and returned by "
|
||||
"glGetCompressedTexImage, but there is no BC/ETC decoder here, so they do not "
|
||||
"reach the texels this level SAMPLES as. Upload through glTexSubImage3D for "
|
||||
"that.",
|
||||
__func__);
|
||||
}
|
||||
|
||||
const SizeT blobSize =
|
||||
textureMipmapObject->GetMipmapCompressedByteSize(textureUploadTarget, static_cast<Uint>(level));
|
||||
const void* existing =
|
||||
textureMipmapObject->MapMipmapCompressedImage(textureUploadTarget, static_cast<Uint>(level));
|
||||
if (blobSize == 0 || existing == nullptr) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"The texture level holds no compressed image to modify."));
|
||||
return;
|
||||
}
|
||||
Vector<Uint8> blob(blobSize);
|
||||
Memcpy(blob.data(), existing, blobSize);
|
||||
|
||||
const SizeT blockByteSize = compressedInfo.blockByteSize;
|
||||
const SizeT levelBlocksX = (static_cast<SizeT>(levelSize.x()) + compressedInfo.blockWidth - 1) /
|
||||
compressedInfo.blockWidth;
|
||||
const SizeT levelBlocksY = (static_cast<SizeT>(levelSize.y()) + compressedInfo.blockHeight - 1) /
|
||||
compressedInfo.blockHeight;
|
||||
const SizeT levelRowBytes = levelBlocksX * blockByteSize;
|
||||
const SizeT levelSliceBytes = levelRowBytes * levelBlocksY;
|
||||
const SizeT regionBlocksX = (static_cast<SizeT>(width) + compressedInfo.blockWidth - 1) /
|
||||
compressedInfo.blockWidth;
|
||||
const SizeT regionBlocksY = (static_cast<SizeT>(height) + compressedInfo.blockHeight - 1) /
|
||||
compressedInfo.blockHeight;
|
||||
const SizeT firstBlockX = static_cast<SizeT>(xoffset) / compressedInfo.blockWidth;
|
||||
const SizeT firstBlockY = static_cast<SizeT>(yoffset) / compressedInfo.blockHeight;
|
||||
const SizeT regionRowBytes = regionBlocksX * blockByteSize;
|
||||
const SizeT regionSliceBytes = regionRowBytes * regionBlocksY;
|
||||
const auto* source = static_cast<const Uint8*>(compressedBytes);
|
||||
for (SizeT slice = 0; slice < static_cast<SizeT>(depth); ++slice) {
|
||||
const SizeT destSliceBase = (static_cast<SizeT>(zoffset) + slice) * levelSliceBytes;
|
||||
for (SizeT row = 0; row < regionBlocksY; ++row) {
|
||||
const SizeT destOffset =
|
||||
destSliceBase + (firstBlockY + row) * levelRowBytes + firstBlockX * blockByteSize;
|
||||
if (destOffset + regionRowBytes > blobSize) break; // a level whose blob predates its size
|
||||
Memcpy(blob.data() + destOffset, source + slice * regionSliceBytes + row * regionRowBytes,
|
||||
regionRowBytes);
|
||||
}
|
||||
}
|
||||
textureMipmapObject->SetMipmapCompressedImage(textureUploadTarget, static_cast<Uint>(level), format,
|
||||
blob.data(), blobSize);
|
||||
}
|
||||
|
||||
// Replaces a block-aligned rectangle of the compressed image glCompressedTexImage2D (or a
|
||||
@@ -4278,15 +4438,83 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
RecordUnsupportedCompressedFormat(__func__);
|
||||
}
|
||||
|
||||
// The three-dimensional twin of CompressedTexImage2D_State, and the same deviation applies: the
|
||||
// blocks are shadowed verbatim for glGetCompressedTexImage while the texels this level SAMPLES
|
||||
// as stay zero, because there is no BC/ETC decoder here. A 3D compressed image is a stack of
|
||||
// `depth` two-dimensional block grids - every format that reaches here has a 4x4x1 block - so
|
||||
// the blob layout is slice-major and CalculateCompressedTextureImageSize already multiplies by
|
||||
// depth.
|
||||
void CompressedTexImage3D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
||||
GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
|
||||
// ======================= Converting ================================
|
||||
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
// Zero block width doubles as "internalformat is not a specific compressed format", which is
|
||||
// the INVALID_ENUM case - one lookup answers both questions.
|
||||
const auto compressedInfo = MG_Util::GetCompressedFormatInfo(internalformat);
|
||||
|
||||
// ===================== Error Checking ==============================
|
||||
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
||||
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
||||
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return;
|
||||
if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return;
|
||||
if (!TextureImpl::ValidateTextureBorderNumber(border)) return;
|
||||
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
||||
if (compressedInfo.blockWidth == 0) {
|
||||
RecordUnsupportedCompressedFormat(__func__);
|
||||
return;
|
||||
}
|
||||
// GL 4.6 core 8.7: imageSize must be exactly the size the format and dimensions imply,
|
||||
// otherwise INVALID_VALUE. This is also the guard that keeps the copy below in bounds.
|
||||
const SizeT expectedImageSize =
|
||||
MG_Util::CalculateCompressedTextureImageSize(compressedInfo, {width, height, depth});
|
||||
if (imageSize < 0 || static_cast<SizeT>(imageSize) != expectedImageSize) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"imageSize does not match the compressed image size."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Object resolution copied from TexImage3D_State rather than routed through
|
||||
// GetTextureObjectByTarget, for the reason CompressedTexImage2D_State gives: a proxy target
|
||||
// is legal here and only CreateOrReplaceProxyTextureObject gives it an object to answer the
|
||||
// level queries from.
|
||||
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
||||
const Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
||||
auto& textureObject =
|
||||
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
||||
: bindingSlot.GetBoundObject();
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||
|
||||
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||
RecordUnsupportedCompressedFormat(__func__);
|
||||
// ======================= Processing ================================
|
||||
const TextureInternalFormat textureInternalFormat =
|
||||
MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
||||
textureObject->SetInternalFormat(textureInternalFormat);
|
||||
|
||||
// A proxy records the format and nothing else - it must never take storage, and it must never
|
||||
// be tagged compressed, or GL_TEXTURE_COMPRESSED_IMAGE_SIZE on a proxy would stop being
|
||||
// INVALID_OPERATION.
|
||||
if (isProxy) return;
|
||||
|
||||
const SizeT internalBpp =
|
||||
MG_Util::GetInternalBytesPerPixel(textureInternalFormat, TexturePixelDataType::UnsignedByte);
|
||||
const SizeT internalBytes =
|
||||
static_cast<SizeT>(width) * static_cast<SizeT>(height) * static_cast<SizeT>(depth) * internalBpp;
|
||||
|
||||
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||
// AllocateStorage clears any compressed image the level used to hold, so this must run before
|
||||
// SetMipmapCompressedImage re-arms it.
|
||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
||||
|
||||
if (!ValidateCompressedUnpackBufferSource(data, expectedImageSize, __func__)) return;
|
||||
const void* compressedBytes = CompressedUnpackSource(data);
|
||||
textureMipmapObject->SetMipmapCompressedImage(textureUploadTarget, level, internalformat, compressedBytes,
|
||||
expectedImageSize);
|
||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
||||
}
|
||||
|
||||
void CompressedTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
||||
@@ -5521,6 +5749,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
free(processedPixels);
|
||||
}
|
||||
|
||||
void CompressedTextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
||||
GLsizei imageSize, const void* data) {
|
||||
auto textureObject = GetTextureObjectByName(texture, __func__);
|
||||
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
||||
CompressedTexSubImage1D_State(target, level, xoffset, width, format, imageSize, data);
|
||||
});
|
||||
}
|
||||
|
||||
void CompressedTextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
||||
GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
|
||||
auto textureObject = GetTextureObjectByName(texture, __func__);
|
||||
@@ -5529,6 +5765,16 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
});
|
||||
}
|
||||
|
||||
void CompressedTextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
||||
const void* data) {
|
||||
auto textureObject = GetTextureObjectByName(texture, __func__);
|
||||
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
||||
CompressedTexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format,
|
||||
imageSize, data);
|
||||
});
|
||||
}
|
||||
|
||||
void TextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
||||
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
|
||||
auto textureObject = GetTextureObjectByName(texture, __func__);
|
||||
|
||||
@@ -37,8 +37,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
GLenum format, GLenum type, const void* pixels);
|
||||
void TextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
||||
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
|
||||
void CompressedTextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
||||
GLsizei imageSize, const void* data);
|
||||
void CompressedTextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
||||
GLsizei height, GLenum format, GLsizei imageSize, const void* data);
|
||||
void CompressedTextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
||||
const void* data);
|
||||
void TextureParameterf(GLuint texture, GLenum pname, GLfloat param);
|
||||
void TextureParameterfv(GLuint texture, GLenum pname, const GLfloat* params);
|
||||
void TextureParameteri(GLuint texture, GLenum pname, GLint param);
|
||||
|
||||
@@ -711,6 +711,62 @@ TEST_F(BufferTest, ClearNamedBufferSubDataRepeatsPattern) {
|
||||
EXPECT_EQ(actual, (Vector<Uint32>{0, pattern, pattern, pattern, 0}));
|
||||
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// GL 4.6 core table 8.2 pairs GL_INT with the non-integer base formats as a signed-normalized
|
||||
// source, so a GL_R8 clear whose pattern arrives as (GL_RED, GL_INT) is legal. The pair used to be
|
||||
// rejected with INVALID_VALUE, which is the first call
|
||||
// KHR-GL45.direct_state_access.buffers_functional makes.
|
||||
TEST_F(BufferTest, ClearNamedBufferSubDataAcceptsSignedNormalizedIntPattern) {
|
||||
GLuint buffer = 0;
|
||||
MobileGL::MG_Impl::GLImpl::CreateBuffers(1, &buffer);
|
||||
|
||||
const Vector<Uint8> initial(24, 0x7F);
|
||||
MobileGL::MG_Impl::GLImpl::NamedBufferStorage(
|
||||
buffer, initial.size(), initial.data(),
|
||||
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
ASSERT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
const GLint zero = 0;
|
||||
MobileGL::MG_Impl::GLImpl::ClearNamedBufferSubData(buffer, GL_R8, 0, sizeof(GLint), GL_RED, GL_INT, &zero);
|
||||
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Vector<Uint8> actual(initial.size());
|
||||
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size());
|
||||
Vector<Uint8> expected(initial);
|
||||
for (SizeT i = 0; i < sizeof(GLint); ++i) expected[i] = 0;
|
||||
EXPECT_EQ(actual, expected);
|
||||
|
||||
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// The same pair on the bound-target entry point: the DSA and the bound call share
|
||||
// ClearBufferRange_State, and a regression in either direction has to show up here too.
|
||||
TEST_F(BufferTest, ClearBufferSubDataAcceptsSignedNormalizedIntPattern) {
|
||||
GLuint buffer = 0;
|
||||
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
|
||||
const Vector<Uint8> initial(8, 0x7F);
|
||||
MobileGL::MG_Impl::GLImpl::BufferData(GL_ARRAY_BUFFER, initial.size(), initial.data(), GL_STATIC_DRAW);
|
||||
// GL_INT is signed-normalized against 2^31-1, so the maximum maps to a saturated GL_R8 texel.
|
||||
const GLint one = 2147483647;
|
||||
MobileGL::MG_Impl::GLImpl::ClearBufferSubData(GL_ARRAY_BUFFER, GL_R8, 0, 4, GL_RED, GL_INT, &one);
|
||||
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Vector<Uint8> actual(initial.size());
|
||||
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size());
|
||||
EXPECT_EQ(actual, (Vector<Uint8>{0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F}));
|
||||
|
||||
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
TEST_F(BufferTest, ClearBufferSubDataInitializesIrisStaticSsboRange) {
|
||||
GLuint buffer = 0;
|
||||
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||
|
||||
@@ -2227,6 +2227,175 @@ TEST_F(TextureTest, CompressedTextureSubImage2DModifiesTheNamedTextureOnly) {
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// 8x8x8 RGTC1: 2x2 blocks of 8 bytes per slice, so a slice is 32 bytes and the stack is 256.
|
||||
constexpr GLsizei kRgtc1Size8x8x8 = 256;
|
||||
constexpr GLsizei kRgtc1Slice8x8 = 32;
|
||||
|
||||
GLuint MakeCompressedRgtc1Texture3D() {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
MG_Impl::GLImpl::CompressedTexImage3D(GL_TEXTURE_3D, 0, GL_COMPRESSED_RED_RGTC1, 8, 8, 8, 0, kRgtc1Size8x8x8,
|
||||
nullptr);
|
||||
return texture;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// glCompressedTexImage3D used to answer GL_INVALID_ENUM to every call, which is what threw
|
||||
// KHR-GL45.direct_state_access.textures_compressed_subimage out with an InternalError: the CTS
|
||||
// asserts no error on it. A 3D compressed image is a stack of per-slice block grids, and the whole
|
||||
// stack has to come back byte for byte.
|
||||
TEST_F(TextureTest, CompressedTexImage3DShadowsTheWholeStackForReadback) {
|
||||
Uint8 whole[kRgtc1Size8x8x8];
|
||||
for (Int i = 0; i < kRgtc1Size8x8x8; ++i) whole[i] = static_cast<Uint8>(i);
|
||||
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
MG_Impl::GLImpl::CompressedTexImage3D(GL_TEXTURE_3D, 0, GL_COMPRESSED_RED_RGTC1, 8, 8, 8, 0, kRgtc1Size8x8x8,
|
||||
whole);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Uint8 stored[kRgtc1Size8x8x8] = {};
|
||||
MG_Impl::GLImpl::GetCompressedTexImage(GL_TEXTURE_3D, 0, stored);
|
||||
EXPECT_EQ(std::memcmp(stored, whole, sizeof(whole)), 0);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
// An imageSize that is not the one the format and the three dimensions imply - the depth axis
|
||||
// is the term a 2D-shaped size calculation would drop.
|
||||
MG_Impl::GLImpl::CompressedTexImage3D(GL_TEXTURE_3D, 0, GL_COMPRESSED_RED_RGTC1, 8, 8, 8, 0, kRgtc1Slice8x8,
|
||||
whole);
|
||||
ExpectSingleGlError(GL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
// Where the incoming blocks land. The box below is one block wide, one block high and two slices
|
||||
// deep, starting at block (1,1) of slice 3: an implementation that dropped the slice stride, the
|
||||
// block-row term or the block-column term puts them somewhere else, and a full-image write would
|
||||
// hide all three.
|
||||
TEST_F(TextureTest, CompressedTexSubImage3DPlacesBlocksSliceBySlice) {
|
||||
const GLuint texture = MakeCompressedRgtc1Texture3D();
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Uint8 zeros[kRgtc1Size8x8x8] = {};
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
kRgtc1Size8x8x8, zeros);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
const Uint8 box[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7};
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 4, 4, 3, 4, 4, 2, GL_COMPRESSED_RED_RGTC1,
|
||||
static_cast<GLsizei>(sizeof(box)), box);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Uint8 expected[kRgtc1Size8x8x8] = {};
|
||||
// slice 3, block row 1, block column 1 -> 3*32 + 1*16 + 1*8, and the same place one slice on.
|
||||
std::memcpy(expected + 3 * kRgtc1Slice8x8 + 16 + 8, box, 8);
|
||||
std::memcpy(expected + 4 * kRgtc1Slice8x8 + 16 + 8, box + 8, 8);
|
||||
|
||||
Uint8 stored[kRgtc1Size8x8x8] = {};
|
||||
MG_Impl::GLImpl::GetCompressedTexImage(GL_TEXTURE_3D, 0, stored);
|
||||
EXPECT_EQ(std::memcmp(stored, expected, sizeof(expected)), 0);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// glCompressedTextureSubImage3D was an exported no-op that raised no error at all. It must reach the
|
||||
// NAMED texture and leave the binding it borrowed exactly as it found it.
|
||||
TEST_F(TextureTest, CompressedTextureSubImage3DModifiesTheNamedTextureOnly) {
|
||||
const GLuint bound = MakeCompressedRgtc1Texture3D();
|
||||
Uint8 boundImage[kRgtc1Size8x8x8];
|
||||
std::memset(boundImage, 0x11, sizeof(boundImage));
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
kRgtc1Size8x8x8, boundImage);
|
||||
|
||||
const GLuint named = MakeCompressedRgtc1Texture3D();
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, bound); // `named` is NOT the bound texture
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Uint8 namedImage[kRgtc1Size8x8x8];
|
||||
std::memset(namedImage, 0x22, sizeof(namedImage));
|
||||
MG_Impl::GLImpl::CompressedTextureSubImage3D(named, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
kRgtc1Size8x8x8, namedImage);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
Uint8 stored[kRgtc1Size8x8x8] = {};
|
||||
MG_Impl::GLImpl::GetCompressedTexImage(GL_TEXTURE_3D, 0, stored);
|
||||
EXPECT_EQ(std::memcmp(stored, boundImage, sizeof(stored)), 0);
|
||||
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, named);
|
||||
std::memset(stored, 0, sizeof(stored));
|
||||
MG_Impl::GLImpl::GetCompressedTexImage(GL_TEXTURE_3D, 0, stored);
|
||||
EXPECT_EQ(std::memcmp(stored, namedImage, sizeof(stored)), 0);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, CompressedTexSubImage3DRejectsTheRegionsGLForbids) {
|
||||
const GLuint texture = MakeCompressedRgtc1Texture3D();
|
||||
Uint8 blocks[kRgtc1Size8x8x8] = {};
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
// A format that is not the one the image is stored in.
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RG_RGTC2, 512, blocks);
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
// A start that is not on a block boundary.
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 2, 0, 0, 4, 8, 8, GL_COMPRESSED_RED_RGTC1, 128, blocks);
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
// A box that runs past the last slice - the depth bound a 2D-shaped range check never applies.
|
||||
MG_Impl::GLImpl::CompressedTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 6, 8, 8, 4, GL_COMPRESSED_RED_RGTC1, 128, blocks);
|
||||
ExpectSingleGlError(GL_INVALID_VALUE);
|
||||
|
||||
(void)texture;
|
||||
}
|
||||
|
||||
// The DSA name rule the CTS's textures_creation pair does not reach for these two entry points: a
|
||||
// name handed out by glGenTextures has no object until it is first bound, so a by-name call on it is
|
||||
// INVALID_OPERATION - and, unlike the stub these replaced, it has to SAY so rather than return
|
||||
// quietly. A glCreateTextures name is a created object and gets past the name check.
|
||||
TEST_F(TextureTest, CompressedTextureSubImage3DRejectsAGeneratedButNeverBoundName) {
|
||||
GLuint generated = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &generated);
|
||||
ASSERT_NE(generated, 0u);
|
||||
DrainPendingGlErrors();
|
||||
|
||||
Uint8 blocks[kRgtc1Size8x8x8] = {};
|
||||
MG_Impl::GLImpl::CompressedTextureSubImage3D(generated, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
kRgtc1Size8x8x8, blocks);
|
||||
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||
|
||||
// A created name is past the name check, so whatever it answers is about the IMAGE (this one
|
||||
// holds none yet), never about the name.
|
||||
GLuint created = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_3D, 1, &created);
|
||||
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
MG_Impl::GLImpl::CompressedTextureSubImage3D(created, 0, 0, 0, 0, 8, 8, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
kRgtc1Size8x8x8, blocks);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION)
|
||||
<< "a created 3D texture with no compressed image is an image error, not a name error";
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// Core GL defines no compressed format for a 1D target, so both the bound and the by-name entry
|
||||
// point have to REFUSE the call. The by-name one used to be an exported no-op that raised nothing,
|
||||
// which is the one answer an application cannot act on.
|
||||
TEST_F(TextureTest, CompressedTextureSubImage1DRefusesLikeTheBoundCall) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_1D, texture);
|
||||
MG_Impl::GLImpl::TexImage1D(GL_TEXTURE_1D, 0, GL_R8, 8, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
|
||||
DrainPendingGlErrors();
|
||||
|
||||
Uint8 blocks[16] = {};
|
||||
MG_Impl::GLImpl::CompressedTexSubImage1D(GL_TEXTURE_1D, 0, 0, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
static_cast<GLsizei>(sizeof(blocks)), blocks);
|
||||
ExpectSingleGlError(GL_INVALID_ENUM);
|
||||
|
||||
MG_Impl::GLImpl::CompressedTextureSubImage1D(texture, 0, 0, 8, GL_COMPRESSED_RED_RGTC1,
|
||||
static_cast<GLsizei>(sizeof(blocks)), blocks);
|
||||
ExpectSingleGlError(GL_INVALID_ENUM);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, CompressedTexSubImage2DRejectsTheRegionsGLForbids) {
|
||||
const GLuint texture = MakeCompressedRgtc1Texture8x8();
|
||||
Uint8 blocks[kRgtc1Size8x8] = {};
|
||||
|
||||
@@ -442,14 +442,30 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
||||
return packed.fieldCount == mapping.channelCount;
|
||||
}
|
||||
|
||||
// GL 4.6 core table 8.2: every unpacked component type pairs with every base format,
|
||||
// with only two exclusions - an integer format takes integer types only, and the two
|
||||
// floating types need a non-integer format. This used to be derived from
|
||||
// GetDirectShadowComponentForType, which answers a different question (is the client
|
||||
// layout byte-identical to some shadow layout) and has no SNorm32 to hand back for
|
||||
// (non-integer format, GL_INT). That legal pair was therefore rejected outright, even
|
||||
// though ConvertUnpackRow decodes it through DecodeComponentToFloat like every other
|
||||
// normalized type - which is what glClearBufferData(GL_R8, GL_RED, GL_INT) needs.
|
||||
switch (type) {
|
||||
case TexturePixelDataType::UnsignedInt5999Rev:
|
||||
case TexturePixelDataType::UnsignedInt101111Rev:
|
||||
return !mapping.isInteger && mapping.channelCount == 3;
|
||||
default: {
|
||||
ShadowComponent component{};
|
||||
return GetDirectShadowComponentForType(type, mapping.isInteger, component);
|
||||
}
|
||||
case TexturePixelDataType::UnsignedByte:
|
||||
case TexturePixelDataType::Byte:
|
||||
case TexturePixelDataType::UnsignedShort:
|
||||
case TexturePixelDataType::Short:
|
||||
case TexturePixelDataType::UnsignedInt:
|
||||
case TexturePixelDataType::Int:
|
||||
return true;
|
||||
case TexturePixelDataType::HalfFloat:
|
||||
case TexturePixelDataType::Float:
|
||||
return !mapping.isInteger;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user