mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18:30 +09:00
[Fix] (DirectGLES): stop the default texture clobbering an aliased real binding
Desktop GL_TEXTURE_1D/1D_ARRAY have no ES equivalent and are emulated on
GL_TEXTURE_2D/2D_ARRAY, so one native binding serves two frontend slots of the
same texture unit. BindCurrentTextures walked the slots in enum order and let
the last one win, which is wrong as soon as one of an aliased pair holds a real
texture and the other holds the unit's default (name 0) object: the default
would be bound over the real texture and the shader sampled an empty texture,
which GL resolves to opaque black.
The default is only skipped while it has never been given an image, so this
needed nothing more than some earlier test in the same glcts process defining
one on texture name 0 - after which every later case that sampled a 1D texture
returned black. That is the mechanism behind a whole family of failures that
only reproduced when another case ran first: texture_lod_basic.lod_selection,
packed_pixels.varied_rectangle.rgba4_format_bgra, shaders.arrays.{return,
unnamed_parameter}.float_vertex and clip_distance.functional all pass in the
full-suite ordering now.
Resolved with a second pass, mirroring the intent the unbind half of the
function already had ("a default alias must not clear a real binding"): real
textures are placed first, then defaults fill only the native targets nothing
else claimed.
This commit is contained in:
@@ -1213,30 +1213,45 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
|
||||
Array<Bool, (SizeT)TextureTarget::TextureTargetCount> boundBackendTargets{};
|
||||
|
||||
for (const auto& bindingSlot : textureUnit.GetAllBindingSlots()) {
|
||||
const auto& textureObject = bindingSlot.GetBoundObject();
|
||||
if (!textureObject) continue;
|
||||
// Two passes over the slots, because desktop 1D/1D-array targets alias ES
|
||||
// 2D/2D-array targets: a unit can hold a real texture on one of an aliased
|
||||
// pair and a default (name 0) object on the other, and one native binding
|
||||
// has to serve both. A default object only carries the app's bind-0 state,
|
||||
// so a real texture always wins the alias - binding it second would leave
|
||||
// the shader sampling an empty texture (an app that gives name 0 an image
|
||||
// then still keeps the default's binding when nothing else claims the
|
||||
// native target). Pass 0 places the real textures, pass 1 fills in the
|
||||
// defaults for native targets that are still unclaimed.
|
||||
for (Int pass = 0; pass < 2; ++pass) {
|
||||
for (const auto& bindingSlot : textureUnit.GetAllBindingSlots()) {
|
||||
const auto& textureObject = bindingSlot.GetBoundObject();
|
||||
if (!textureObject) continue;
|
||||
|
||||
// An image-less default texture is the frontend's bind-0 state. Defer native
|
||||
// unbinding until all slots have been considered: desktop 1D/1D-array targets
|
||||
// alias ES 2D/2D-array targets, so a default alias must not clear a real binding.
|
||||
if (MG_State::GLState::IsUndefinedDefaultTexture(textureObject.get())) continue;
|
||||
// An image-less default texture is the frontend's bind-0 state. Defer native
|
||||
// unbinding until all slots have been considered: a default alias must not
|
||||
// clear a real binding either.
|
||||
if (MG_State::GLState::IsUndefinedDefaultTexture(textureObject.get())) continue;
|
||||
|
||||
auto target = textureObject->GetTarget();
|
||||
if (!TextureImpl::IsSupportedTextureTarget(target)) {
|
||||
MGLOG_D(" Texture target %s is not supported, skipping.",
|
||||
MG_Util::ConvertTextureTargetToString(target).c_str());
|
||||
continue;
|
||||
const Bool isDefaultObject = textureObject->GetExternalIndex() == 0;
|
||||
if (isDefaultObject != (pass == 1)) continue;
|
||||
|
||||
auto target = textureObject->GetTarget();
|
||||
if (!TextureImpl::IsSupportedTextureTarget(target)) {
|
||||
MGLOG_D(" Texture target %s is not supported, skipping.",
|
||||
MG_Util::ConvertTextureTargetToString(target).c_str());
|
||||
continue;
|
||||
}
|
||||
const auto backendTarget = TextureImpl::MapToBackendTextureTarget(target);
|
||||
if (isDefaultObject && boundBackendTargets[static_cast<SizeT>(backendTarget)]) continue;
|
||||
const GLenum targetGL = TextureImpl::ConvertTextureTargetToBackendGLEnum(target);
|
||||
|
||||
// Bind texture object
|
||||
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
|
||||
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue;
|
||||
|
||||
backendTextureIt->second->Bind(targetGL, unit);
|
||||
boundBackendTargets[static_cast<SizeT>(backendTarget)] = true;
|
||||
}
|
||||
const GLenum targetGL = TextureImpl::ConvertTextureTargetToBackendGLEnum(target);
|
||||
|
||||
// Bind texture object
|
||||
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
|
||||
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue;
|
||||
|
||||
backendTextureIt->second->Bind(targetGL, unit);
|
||||
const auto backendTarget = TextureImpl::MapToBackendTextureTarget(target);
|
||||
boundBackendTargets[static_cast<SizeT>(backendTarget)] = true;
|
||||
}
|
||||
|
||||
// Clear each native target that has no resolved frontend binding. This is the backend
|
||||
|
||||
Reference in New Issue
Block a user