mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Fix] (MG_Backend/DirectGLES, MG_Impl/GLImpl, MG_Util): GL CTS packed_pixels + texture_swizzle readback overhaul - canonical shadow layouts for legacy sized/unsized/packed internal formats (RGB5->RGB565, RGB10/12->RGB16, RGBA2->RGBA4, RGB10_A2(UI)/RGB9_E5/R11F_G11F_B10F packed-word shadows with per-texel encode/decode incl. 5_9_9_9_REV and 10F_11F_11F_REV client types), GL_UNSIGNED_INT_10_10_10_2 pixel type mapping, conversion-first GetTexImage with CPU-shadow fallback for non-attachable formats and stale-temp-FBO detach, narrow implementation read pairs + SNORM read candidates + 2_10_10_10_REV wide-read decode with RGBA expansion, PACK image/skip and SWAP_BYTES honored on the CPU repack (never in ES), state-reset conformance (default-texture TexParameter/TexImage/TexBuffer no-ops, renderbuffer 0 unbind, vertex attrib 0 current value, ActiveTexture up to combined units, UBO binding count clamp), FramebufferTexture3D/TextureLayer slice attachments via glFramebufferTextureLayer, capability-driven FBO UNSUPPORTED for non-renderable colors, ReadPixels integer-ness mismatch error, single-value texture swizzle validation, and DirectGLES 1D/1D-array/2D-array texture emulation (2D/2D-array backend targets matching SPIRV-Cross ES 1D-as-2D shaders)
This commit is contained in:
@@ -60,6 +60,87 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether the backend can actually attach this color format to a framebuffer. Preferred
|
||||
// source of truth is the backend's probed format-capability cache (real glCheckFramebufferStatus
|
||||
// probes, so extensions like EXT_render_snorm are respected). Formats a probe-less backend
|
||||
// cannot answer for fall back to a conservative static list of formats no ES driver renders to:
|
||||
// shared-exponent, SNORM, three-channel norm16/float32/sRGB and three-channel integer formats.
|
||||
// Desktop GL treats those as texture-only too (not in the GL 3.3 required-renderable list), so
|
||||
// reporting GL_FRAMEBUFFER_UNSUPPORTED for them is legal.
|
||||
Bool IsColorInternalFormatRenderable(TextureInternalFormat format) {
|
||||
const SizeT formatIndex = static_cast<SizeT>(format);
|
||||
if (MG_Backend::pActiveBackendObject && formatIndex < MG_Backend::kFormatCapabilityFormatCount) {
|
||||
const auto& cache = MG_Backend::pActiveBackendObject->GetFormatCapabilities();
|
||||
const SizeT sentinelFormat = static_cast<SizeT>(TextureInternalFormat::RGBA8);
|
||||
Bool cachePopulated = false;
|
||||
for (SizeT targetIndex = 0; targetIndex < MG_Backend::kFormatCapabilityTargetCount && !cachePopulated;
|
||||
++targetIndex) {
|
||||
cachePopulated = MG_Backend::HasFormatCapability(cache.FullCaps[targetIndex][sentinelFormat],
|
||||
MG_Backend::FormatCapability::Creatable);
|
||||
}
|
||||
if (cachePopulated) {
|
||||
for (SizeT targetIndex = 0; targetIndex < MG_Backend::kFormatCapabilityTargetCount;
|
||||
++targetIndex) {
|
||||
if (MG_Backend::HasFormatCapability(cache.FullCaps[targetIndex][formatIndex],
|
||||
MG_Backend::FormatCapability::FramebufferRenderable) ||
|
||||
MG_Backend::HasFormatCapability(cache.CaveatCaps[targetIndex][formatIndex],
|
||||
MG_Backend::FormatCapability::FramebufferRenderable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (format) {
|
||||
case TextureInternalFormat::RGB9E5:
|
||||
case TextureInternalFormat::R8Snorm:
|
||||
case TextureInternalFormat::RG8Snorm:
|
||||
case TextureInternalFormat::RGB8Snorm:
|
||||
case TextureInternalFormat::RGBA8Snorm:
|
||||
case TextureInternalFormat::R16Snorm:
|
||||
case TextureInternalFormat::RG16Snorm:
|
||||
case TextureInternalFormat::RGB16Snorm:
|
||||
case TextureInternalFormat::RGBA16Snorm:
|
||||
case TextureInternalFormat::RGB16:
|
||||
case TextureInternalFormat::RGB10: // stored as RGB16
|
||||
case TextureInternalFormat::RGB12: // stored as RGB16
|
||||
case TextureInternalFormat::RGB16F:
|
||||
case TextureInternalFormat::RGB32F:
|
||||
case TextureInternalFormat::RGB8I:
|
||||
case TextureInternalFormat::RGB8UI:
|
||||
case TextureInternalFormat::RGB16I:
|
||||
case TextureInternalFormat::RGB16UI:
|
||||
case TextureInternalFormat::RGB32I:
|
||||
case TextureInternalFormat::RGB32UI:
|
||||
case TextureInternalFormat::SRGB8:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Bool HasNonRenderableColorAttachment(const MG_State::GLState::FramebufferObject& framebufferObject) {
|
||||
const auto& attachments = framebufferObject.GetAllAttachmentObjects();
|
||||
for (SizeT i = 0; i < attachments.size(); ++i) {
|
||||
const auto type = static_cast<FramebufferAttachmentType>(i);
|
||||
if (type < FramebufferAttachmentType::Color0 || type > FramebufferAttachmentType::Color31) {
|
||||
continue;
|
||||
}
|
||||
const auto& attachment = attachments[i];
|
||||
if (!attachment.IsValid()) continue;
|
||||
TextureInternalFormat format = TextureInternalFormat::Unknown;
|
||||
if (attachment.IsTexture() && attachment.GetTexture()) {
|
||||
format = attachment.GetTexture()->GetFormat();
|
||||
} else if (attachment.IsRenderbuffer() && attachment.GetRenderbuffer()) {
|
||||
format = attachment.GetRenderbuffer()->GetInternalFormat();
|
||||
}
|
||||
if (format != TextureInternalFormat::Unknown && !IsColorInternalFormatRenderable(format)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RecordUnsupportedFramebufferTextureAttachmentError(const char* functionName, const char* detail) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
@@ -556,6 +637,62 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return framebufferObject;
|
||||
}
|
||||
|
||||
// Attaches a single layer/slice of a 3D or array texture. The attachment model stores the layer
|
||||
// index; the DirectGLES backend attaches it with glFramebufferTextureLayer.
|
||||
static void AttachFramebufferTextureLayer(const char* functionName, GLenum target, GLenum attachment,
|
||||
GLuint texture, GLint level, GLint layer,
|
||||
TextureUploadTarget textureUploadTarget) {
|
||||
if (target == GL_FRAMEBUFFER) {
|
||||
target = GL_DRAW_FRAMEBUFFER;
|
||||
}
|
||||
if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
|
||||
AttachFramebufferTextureLayer(functionName, target, GL_DEPTH_ATTACHMENT, texture, level, layer,
|
||||
textureUploadTarget);
|
||||
AttachFramebufferTextureLayer(functionName, target, GL_STENCIL_ATTACHMENT, texture, level, layer,
|
||||
textureUploadTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
const FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment);
|
||||
const FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
|
||||
if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return;
|
||||
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
|
||||
if (!TextureImpl::ValidateTextureName(texture, true)) return;
|
||||
|
||||
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
|
||||
auto& framebufferObject = bindingSlot.GetBoundObject();
|
||||
if (!framebufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||
"Framebuffer target is bound to no framebuffer object."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture == 0) {
|
||||
framebufferObject->Detach(attachmentType);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||
if (!textureObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||
std::format("Texture object {} is not valid.", texture)));
|
||||
return;
|
||||
}
|
||||
if (layer < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName, "Layer must be non-negative."));
|
||||
return;
|
||||
}
|
||||
|
||||
framebufferObject->AttachTexture(attachmentType, textureObject, textureUploadTarget, level, layer,
|
||||
/*layered=*/false);
|
||||
}
|
||||
|
||||
void FramebufferTextureLayer_State(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
|
||||
if (texture == 0) {
|
||||
const TextureUploadTarget detachTarget = TextureUploadTarget::Texture2D;
|
||||
@@ -563,10 +700,31 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<void>(layer);
|
||||
RecordUnsupportedFramebufferTextureAttachmentError(
|
||||
__func__,
|
||||
"Layered framebuffer texture attachments are not represented by the current framebuffer attachment model.");
|
||||
auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||
if (!textureObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
std::format("Texture object {} is not valid.", texture)));
|
||||
return;
|
||||
}
|
||||
TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown;
|
||||
switch (textureObject->GetTarget()) {
|
||||
case TextureTarget::Texture3D:
|
||||
textureUploadTarget = TextureUploadTarget::Texture3D;
|
||||
break;
|
||||
case TextureTarget::Texture2DArray:
|
||||
textureUploadTarget = TextureUploadTarget::Texture2DArray;
|
||||
break;
|
||||
case TextureTarget::Texture2DMultisampleArray:
|
||||
textureUploadTarget = TextureUploadTarget::Texture2DMultisampleArray;
|
||||
break;
|
||||
default:
|
||||
RecordUnsupportedFramebufferTextureAttachmentError(
|
||||
__func__, "FramebufferTextureLayer requires a 3D, 2D array or 2D multisample array texture.");
|
||||
return;
|
||||
}
|
||||
AttachFramebufferTextureLayer(__func__, target, attachment, texture, level, layer, textureUploadTarget);
|
||||
}
|
||||
|
||||
void FramebufferTexture3D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
|
||||
@@ -578,10 +736,15 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<void>(zoffset);
|
||||
RecordUnsupportedFramebufferTextureAttachmentError(
|
||||
__func__,
|
||||
"3D framebuffer texture slice attachments are not represented by the current framebuffer attachment model.");
|
||||
if (textarget != GL_TEXTURE_3D) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"FramebufferTexture3D requires GL_TEXTURE_3D."));
|
||||
return;
|
||||
}
|
||||
AttachFramebufferTextureLayer(__func__, target, attachment, texture, level, zoffset,
|
||||
TextureUploadTarget::Texture3D);
|
||||
}
|
||||
|
||||
void FramebufferTexture2D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
|
||||
@@ -1252,6 +1415,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT :
|
||||
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
|
||||
}
|
||||
if (HasNonRenderableColorAttachment(*framebufferObject)) {
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
}
|
||||
if (IsActiveBackendDirectVulkan() &&
|
||||
IsUnsupportedFramebufferForDirectVulkan(*framebufferObject)) {
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
@@ -1277,6 +1443,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT :
|
||||
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
|
||||
}
|
||||
if (HasNonRenderableColorAttachment(*framebufferObject)) {
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
}
|
||||
if (IsActiveBackendDirectVulkan() &&
|
||||
IsUnsupportedFramebufferForDirectVulkan(*framebufferObject)) {
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
@@ -1626,8 +1795,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check framebuffer completeness
|
||||
if (!framebufferObject->CheckCompleteness()) {
|
||||
// Check framebuffer completeness (including formats the ES pipeline cannot attach)
|
||||
if (!framebufferObject->CheckCompleteness() || HasNonRenderableColorAttachment(*framebufferObject)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidFramebufferOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete"));
|
||||
@@ -1679,6 +1848,26 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
"No color buffer for color format"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// GL 3.3 section 4.3.1: GL_INVALID_OPERATION if format is an integer format and the read
|
||||
// buffer is not an integer format, or vice versa (GL CTS packed_pixels expects the error
|
||||
// for every *_INTEGER readback from a normalized attachment).
|
||||
const auto& readAttachment = framebufferObject->GetAttachment(readBuffer);
|
||||
TextureInternalFormat attachmentFormat = TextureInternalFormat::Unknown;
|
||||
if (readAttachment.IsTexture() && readAttachment.GetTexture()) {
|
||||
attachmentFormat = readAttachment.GetTexture()->GetFormat();
|
||||
} else if (readAttachment.IsRenderbuffer() && readAttachment.GetRenderbuffer()) {
|
||||
attachmentFormat = readAttachment.GetRenderbuffer()->GetInternalFormat();
|
||||
}
|
||||
if (attachmentFormat != TextureInternalFormat::Unknown &&
|
||||
TextureImpl::IsIntegerColorInputFormat(textureInputFormat) !=
|
||||
TextureImpl::IsIntegerColorInternalFormat(attachmentFormat)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
|
||||
"Integer-ness of format does not match the read buffer"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Packed-type/format pairing (GL CTS packed_pixels: e.g. GL_RED with GL_UNSIGNED_SHORT_5_6_5 must
|
||||
|
||||
@@ -76,7 +76,8 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
|
||||
}
|
||||
|
||||
Bool ValidateRenderbufferName(Uint index, Bool allowZero) {
|
||||
if (index == 0 && !allowZero) {
|
||||
if (index == 0) {
|
||||
if (allowZero) return true; // unbind / detach never needs a live object
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName",
|
||||
|
||||
@@ -1894,7 +1894,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
*params = dynamicParameters.MaxTextureSize;
|
||||
break;
|
||||
case GL_MAX_UNIFORM_BUFFER_BINDINGS:
|
||||
*params = std::max(dynamicParameters.MaxUniformBufferBindings, kFrontendMinUniformBufferBindings);
|
||||
// Never advertise more bindings than the state layer's indexed-binding array can track
|
||||
// (BufferState::BufferBindingPointCount): the GL CTS state reset calls glBindBufferBase
|
||||
// on every advertised index and expects no error.
|
||||
*params = std::clamp(dynamicParameters.MaxUniformBufferBindings, kFrontendMinUniformBufferBindings,
|
||||
static_cast<GLint>(MG_State::GLState::BufferBindingPointCount));
|
||||
break;
|
||||
case GL_MAX_UNIFORM_BLOCK_SIZE:
|
||||
*params = dynamicParameters.MaxUniformBlockSize;
|
||||
|
||||
@@ -404,13 +404,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
"2D multisample textures must use depth 1."));
|
||||
return false;
|
||||
}
|
||||
if (textureTarget == TextureTarget::Texture2DMultisampleArray && depth == 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
||||
"2D multisample array textures must have at least one layer."));
|
||||
return false;
|
||||
}
|
||||
// depth == 0 (like width/height == 0) deallocates the image and is not an error:
|
||||
// the GL CTS state reset calls TexImage3DMultisample with all-zero sizes.
|
||||
|
||||
const Int maxSamples = GetMaxSupportedTextureSamples(textureInternalFormat);
|
||||
if (samples > maxSamples) {
|
||||
@@ -557,6 +552,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_SWIZZLE_A: {
|
||||
auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname);
|
||||
auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam(param);
|
||||
if (swizzleValue == TextureSwizzleParam::Unknown) {
|
||||
// GL CTS texture_swizzle.api_errors: single-value TexParameter* with a value outside
|
||||
// [RED, GREEN, BLUE, ALPHA, ZERO, ONE] must raise GL_INVALID_ENUM.
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Invalid texture swizzle value."));
|
||||
return;
|
||||
}
|
||||
textureObject->SetSwizzleParam(swizzleParam, swizzleValue);
|
||||
break;
|
||||
}
|
||||
@@ -734,6 +737,22 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
}
|
||||
|
||||
// Texture-parameter lookups must not raise GL_INVALID_OPERATION when the default texture
|
||||
// (name 0) is bound: glTexParameter* on default textures is legal GL (the GL CTS state reset
|
||||
// sets swizzles/levels on texture 0 for every unit x target and expects glGetError() to stay
|
||||
// clean). Parameters set on default textures are accepted as a silent no-op.
|
||||
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByTargetForParameter(
|
||||
TextureUploadTarget textureUploadTarget, TextureTarget textureTarget) {
|
||||
if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) {
|
||||
return TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget);
|
||||
}
|
||||
if (textureTarget == TextureTarget::Unknown) {
|
||||
return nullTextureObject;
|
||||
}
|
||||
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
return activeUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
||||
}
|
||||
|
||||
void GenerateMipmap_Backend(GLenum target) {
|
||||
MG_Backend::gBackendFunctionsTable.GL.GenerateMipmap(target);
|
||||
}
|
||||
@@ -1041,7 +1060,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
|
||||
// ======================= Processing ================================
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
|
||||
switch (pname) {
|
||||
@@ -1074,6 +1093,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_SWIZZLE_A: {
|
||||
auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname);
|
||||
auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam((GLenum)param);
|
||||
if (swizzleValue == TextureSwizzleParam::Unknown) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Invalid texture swizzle value."));
|
||||
return;
|
||||
}
|
||||
textureObject->SetSwizzleParam(swizzleParam, swizzleValue);
|
||||
break;
|
||||
}
|
||||
@@ -1120,7 +1145,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
|
||||
// ======================= Processing ================================
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
|
||||
TextureParameterObject_State(textureObject, pname, param, __func__);
|
||||
@@ -1136,7 +1161,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
|
||||
// ======================= Processing ================================
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
SetTextureBorderColorFromFloats(textureObject, params);
|
||||
break;
|
||||
@@ -1144,7 +1169,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_SWIZZLE_RGBA: {
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
GLint signedParams[4] = {static_cast<GLint>(params[0]), static_cast<GLint>(params[1]),
|
||||
static_cast<GLint>(params[2]), static_cast<GLint>(params[3])};
|
||||
@@ -1167,7 +1192,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
|
||||
// ======================= Processing ================================
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
SetTextureBorderColorFromInts(textureObject, params);
|
||||
break;
|
||||
@@ -1175,7 +1200,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_SWIZZLE_RGBA: {
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
if (!SetTextureSwizzleParamsFromInts(textureObject, params, __func__)) {
|
||||
return;
|
||||
@@ -1193,7 +1218,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_BORDER_COLOR: {
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
SetTextureBorderColorFromIntegerInts(textureObject, params);
|
||||
break;
|
||||
@@ -1201,7 +1226,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_SWIZZLE_RGBA: {
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
GLint signedParams[4] = {static_cast<GLint>(params[0]), static_cast<GLint>(params[1]),
|
||||
static_cast<GLint>(params[2]), static_cast<GLint>(params[3])};
|
||||
@@ -1221,7 +1246,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_BORDER_COLOR: {
|
||||
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
SetTextureBorderColorFromUnsignedInts(textureObject, params);
|
||||
break;
|
||||
@@ -1232,7 +1257,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||
|
||||
// ======================= Processing ================================
|
||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||
auto& textureObject = GetTextureObjectByTargetForParameter(textureUploadTarget, textureTarget);
|
||||
if (!textureObject) return;
|
||||
|
||||
Vec4<TextureSwizzleParam> swizzleParams;
|
||||
@@ -1283,6 +1308,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
auto& textureObject =
|
||||
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
||||
: bindingSlot.GetBoundObject();
|
||||
// Respecifying the default texture (name 0) is legal GL (the GL CTS state reset resets it to
|
||||
// zero size); accept it as a silent no-op since default textures carry no storage here.
|
||||
if (!isProxy && !textureObject) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
@@ -1324,6 +1352,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
auto& textureObject =
|
||||
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
||||
: bindingSlot.GetBoundObject();
|
||||
// Respecifying the default texture (name 0) is legal GL (the GL CTS state reset resets it to
|
||||
// zero size); accept it as a silent no-op since default textures carry no storage here.
|
||||
if (!isProxy && !textureObject) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
@@ -1402,6 +1433,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
: bindingSlot.GetBoundObject();
|
||||
|
||||
// ===================== Error Checking ==============================
|
||||
// Respecifying the default texture (name 0) is legal GL (the GL CTS state reset resets every
|
||||
// default texture to zero size); accept it as a silent no-op.
|
||||
if (!isProxy && !textureObject) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||
|
||||
@@ -1521,6 +1555,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
: bindingSlot.GetBoundObject();
|
||||
|
||||
// ===================== Error Checking ==============================
|
||||
// Respecifying the default texture (name 0) is legal GL (the GL CTS state reset resets every
|
||||
// default texture to zero size); accept it as a silent no-op.
|
||||
if (!isProxy && !textureObject) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||
|
||||
@@ -1625,6 +1662,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
auto& textureObject =
|
||||
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
||||
: bindingSlot.GetBoundObject();
|
||||
// Respecifying the default texture (name 0) is legal GL; accept it as a silent no-op.
|
||||
if (!isProxy && !textureObject) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||
|
||||
@@ -1680,13 +1719,19 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
||||
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
||||
// TODO: make sure `internalformat` is in one of supported format for TexBuffer
|
||||
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
if (!bufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"`buffer` is not zero and is not the name of an existing buffer object."));
|
||||
return;
|
||||
// buffer == 0 is a legal detach (the GL CTS state reset calls glTexBuffer(..., 0) and
|
||||
// expects no error); only a non-zero name that does not exist is GL_INVALID_OPERATION.
|
||||
SharedPtr<MG_State::GLState::BufferObject> bufferObject;
|
||||
if (buffer != 0) {
|
||||
bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
if (!bufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"MG_Impl/GLImpl", __func__,
|
||||
"`buffer` is not zero and is not the name of an existing buffer object."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================= Processing ================================
|
||||
@@ -1695,6 +1740,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
auto& textureObject = bindingSlot.GetBoundObject();
|
||||
|
||||
// ===================== Error Checking ==============================
|
||||
// Detaching from the default texture (name 0) is a silent no-op.
|
||||
if (!textureObject && buffer == 0) return;
|
||||
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
||||
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
@@ -2572,14 +2619,22 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
|
||||
void ActiveTexture_State(GLenum texture) {
|
||||
// ===================== Error Checking ==============================
|
||||
if (texture < GL_TEXTURE0 || texture > GL_TEXTURE31) {
|
||||
// The valid range is [GL_TEXTURE0, GL_TEXTURE0 + GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS):
|
||||
// the GL CTS state reset iterates every advertised combined unit, so rejecting units the
|
||||
// implementation itself reports would leave a sticky GL_INVALID_ENUM behind.
|
||||
Int maxCombinedUnits = static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
||||
if (MG_Backend::pActiveBackendObject) {
|
||||
maxCombinedUnits = std::min(
|
||||
maxCombinedUnits, MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxCombinedTextureImageUnits);
|
||||
}
|
||||
if (texture < GL_TEXTURE0 || static_cast<Int>(texture - GL_TEXTURE0) >= maxCombinedUnits) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"MG_Impl/GLImpl", "ActiveTexture_State",
|
||||
std::format("Texture must be one of GL_TEXTUREi, where i is in the range 0 to 31, but got "
|
||||
std::format("Texture must be one of GL_TEXTUREi, where i is in the range 0 to {}, but got "
|
||||
"invalid enum: 0x{:X}, which may stand for unit {}.",
|
||||
texture, texture - GL_TEXTURE0)));
|
||||
maxCombinedUnits - 1, texture, texture - GL_TEXTURE0)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Bool IsIntegerColorInputFormat(TextureInputFormat format) {
|
||||
Bool IsIntegerColorInputFormat(TextureInputFormat format) {
|
||||
return format == TextureInputFormat::RInteger || format == TextureInputFormat::RGInteger ||
|
||||
format == TextureInputFormat::RGBInteger || format == TextureInputFormat::BGRInteger ||
|
||||
format == TextureInputFormat::RGBAInteger || format == TextureInputFormat::BGRAInteger ||
|
||||
@@ -183,7 +183,7 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
||||
format == TextureInputFormat::AlphaInteger;
|
||||
}
|
||||
|
||||
static Bool IsIntegerColorInternalFormat(TextureInternalFormat internalFormat) {
|
||||
Bool IsIntegerColorInternalFormat(TextureInternalFormat internalFormat) {
|
||||
switch (internalFormat) {
|
||||
case TextureInternalFormat::R8I:
|
||||
case TextureInternalFormat::R8UI:
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
||||
Bool ValidateTextureSizeRange(Int width, Int height, Int depth);
|
||||
Bool ValidateTextureInternalFormat(TextureInternalFormat format);
|
||||
Bool ValidateTextureBorderNumber(Int border);
|
||||
Bool IsIntegerColorInputFormat(TextureInputFormat format);
|
||||
Bool IsIntegerColorInternalFormat(TextureInternalFormat internalFormat);
|
||||
Bool ValidateClientFormatTypePairing(TextureInputFormat format, TexturePixelDataType type);
|
||||
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format,
|
||||
TextureInternalFormat internalFormat,
|
||||
|
||||
@@ -78,15 +78,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
|
||||
static bool ValidateCurrentVertexAttribIndex(GLuint index, const char* funcName) {
|
||||
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return false;
|
||||
if (index == 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
|
||||
"Generic vertex attribute 0 current value cannot be modified."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// Core GL allows setting the current value of every generic attribute, including 0
|
||||
// (the GL CTS state reset calls glVertexAttrib4f(0, ...) and expects no error).
|
||||
static_cast<void>(funcName);
|
||||
return VertexArrayImpl::ValidateVertexAttributeIndex(index);
|
||||
}
|
||||
|
||||
static bool TryGetVertexAttribute(GLuint index, const MG_State::GLState::VertexAttribute** outAttr) {
|
||||
|
||||
Reference in New Issue
Block a user