mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
[Fix] (MG_State, DirectVulkan): apply the incomplete-texture rule on Magma too
The completeness rule itself is GL's, not a backend's, so it now reads as one question both backends ask - SamplesAsIncompleteTexture(texture, effective sampler) - and each answers in whatever way it already expresses "nothing is bound at this sampler". DirectGLES leaves the native target unbound; Magma has a fallback texture for exactly that case and now routes an incomplete texture to it. The fallback's texel had never been written, so it read whatever its freshly allocated storage held. GL is specific here: an incomplete texture - and a sampler with nothing bound - reads (0, 0, 0, 1). It says so now, which is what makes KHR-GL40.texture_gather.incomplete-texture-last-comp (it gathers the alpha) meaningful rather than accidentally right.
This commit is contained in:
@@ -1621,9 +1621,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
const auto& effectiveSampler = textureUnit.GetSamplerObject()
|
||||
? textureUnit.GetSamplerObject()
|
||||
: textureObject->GetSamplerObject();
|
||||
const Bool mipmappedFilter =
|
||||
effectiveSampler && effectiveSampler->GetMipmapMode() != SamplerMipmapMode::None;
|
||||
if (!MG_State::GLState::IsMipmapCompleteForFilter(textureObject.get(), mipmappedFilter)) {
|
||||
if (MG_State::GLState::SamplesAsIncompleteTexture(textureObject.get(),
|
||||
effectiveSampler.get())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const auto& samplerOverride = textureUnit.GetSamplerObject();
|
||||
const auto preferredTarget = programObj.samplerTextureTargetByBinding[binding];
|
||||
SharedPtr<MG_State::GLState::ITextureObject> fallbackHolder;
|
||||
// A texture that fails the completeness rules for the filter in effect reads
|
||||
// (0, 0, 0, 1), which is exactly what the fallback texture holds - so it takes the
|
||||
// same route as a sampler with nothing bound.
|
||||
if (texture != nullptr &&
|
||||
MG_State::GLState::SamplesAsIncompleteTexture(
|
||||
texture, samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get())) {
|
||||
texture = nullptr;
|
||||
}
|
||||
if (texture == nullptr) {
|
||||
fallbackHolder = GetFallbackTexture(preferredTarget);
|
||||
texture = fallbackHolder.get();
|
||||
@@ -769,6 +777,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
fallbackTexture->SetInternalFormat(TextureInternalFormat::RGBA8);
|
||||
fallbackTexture->AllocateStorage(TextureUploadTarget::Texture2D, 0,
|
||||
{.texelSize = {1, 1, 1}, .byteSize = 4});
|
||||
// (0, 0, 0, 1): what GL reads from a texture that is not complete, and the only
|
||||
// sensible answer for a sampler with nothing bound.
|
||||
static Uint8 kOpaqueBlackTexel[4] = {0, 0, 0, 255};
|
||||
fallbackTexture->UpdateMipmapSubData(TextureUploadTarget::Texture2D, 0,
|
||||
{kOpaqueBlackTexel, sizeof(kOpaqueBlackTexel)});
|
||||
fallbackTexture->MarkStorageDirty(TextureUploadTarget::Texture2D, 0, true);
|
||||
m_fallbackTexture2D = fallbackTexture;
|
||||
}
|
||||
|
||||
@@ -348,6 +348,12 @@ namespace MobileGL {
|
||||
|
||||
// TODO: add other texture types as needed
|
||||
|
||||
Bool SamplesAsIncompleteTexture(const ITextureObject* texture, const SamplerObject* effectiveSampler) {
|
||||
const Bool mipmapped =
|
||||
effectiveSampler != nullptr && effectiveSampler->GetMipmapMode() != SamplerMipmapMode::None;
|
||||
return !IsMipmapCompleteForFilter(texture, mipmapped);
|
||||
}
|
||||
|
||||
Bool IsMipmapCompleteForFilter(const ITextureObject* texture, Bool mipmapped) {
|
||||
if (texture == nullptr) return true;
|
||||
if (!texture->IsComplete()) return false;
|
||||
|
||||
@@ -164,6 +164,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
// IsComplete() already covers. Sampling an incomplete texture returns (0, 0, 0, 1).
|
||||
Bool IsMipmapCompleteForFilter(const ITextureObject* texture, Bool mipmapped);
|
||||
|
||||
// The rule above asked as the backends need it: does a lookup on this texture read
|
||||
// (0, 0, 0, 1) instead of its contents? `effectiveSampler` is the sampler object bound
|
||||
// to the unit when there is one, otherwise the texture's own. A backend answers yes by
|
||||
// routing the texture to whatever it already uses for "nothing is bound there".
|
||||
Bool SamplesAsIncompleteTexture(const ITextureObject* texture, const SamplerObject* effectiveSampler);
|
||||
|
||||
inline const TextureObjectMipmap* AsMipmapTexture(const ITextureObject* texture) {
|
||||
return (texture && texture->GetStorageType() == TextureStorageType::Mipmap)
|
||||
? static_cast<const TextureObjectMipmap*>(texture)
|
||||
|
||||
Reference in New Issue
Block a user