Files
MobileGL/MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp
T
BZLZHH dd60ff39ce [Feat] (MG_State, MG_Impl, MG_Backend, MG_Util): make the border colour real sampler state
glGetSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR) raised INVALID_ENUM,
because MobileGL kept the border colour on the texture object and
GetSamplerParam_State had no case for it at all. That is the first thing
direct_state_access.samplers_defaults asks, so the case threw before reaching
any of the defaults it was written to check.

GL 4.6 core table 23.18 lists TEXTURE_BORDER_COLOR as sampler state, so it moves
to SamplerParameters and TextureObjectBase reaches it through the SamplerObject
it already owns - one source of truth, and a sampler object bound over a texture
now supplies its own border colour, which is what GL says should happen. The
texture params version still moves on a write, because the DirectGLES texture
sync memoises on it. glSamplerParameter{fv,Iiv,Iuiv} and their getters read and
write all four components in whichever representation the caller used, and the
three representations are kept in step so any getter has an answer. The bogus
[0,1] and [0,255] range checks are gone: GL clamps a border colour when a
fixed-point format is sampled, it does not reject it.

DirectVulkan's ResolveVkBorderColor now reads the sampler rather than the
texture. DirectGLES gained a glSamplerParameterfv in its sampler sync, and both
that and the pre-existing glTexParameterfv are gated on a new
SupportsTextureBorderClamp capability - ES 3.2 core, or EXT/OES_texture_border_clamp
before it - since without the extension every such call is INVALID_ENUM on the
driver. DriverPost gains the matching row per the POST rule, saying what a user
actually loses when it is missing.

Takes direct_state_access.samplers_defaults from failing to passing on both
backends.
2026-08-05 04:45:55 -04:00

223 lines
8.4 KiB
C++

// MobileGL - MobileGL/MG_State/GLState/SamplerState/SamplerObject.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "SamplerObject.h"
#include <atomic>
namespace MobileGL {
namespace MG_State {
namespace GLState {
static std::atomic<Uint64> s_nextSamplerLifetimeId = 1;
Uint64 SamplerObject::AllocateLifetimeId() {
return s_nextSamplerLifetimeId.fetch_add(1, std::memory_order_relaxed);
}
SamplerObject::SamplerObject(Uint externalIndex)
: m_externalIndex(externalIndex), m_lifetimeId(AllocateLifetimeId()) {}
void SamplerObject::SetWrapS(SamplerWrapMode mode) {
if (mode == m_samplerParameters.wrapS) return;
m_samplerParameters.wrapS = mode;
++m_version;
}
void SamplerObject::SetWrapT(SamplerWrapMode mode) {
if (mode == m_samplerParameters.wrapT) return;
m_samplerParameters.wrapT = mode;
++m_version;
}
void SamplerObject::SetWrapR(SamplerWrapMode mode) {
if (mode == m_samplerParameters.wrapR) return;
m_samplerParameters.wrapR = mode;
++m_version;
}
void SamplerObject::SetMinFilter(SamplerFilterMode mode) {
if (mode == m_samplerParameters.minFilter) return;
m_samplerParameters.minFilter = mode;
++m_version;
}
void SamplerObject::SetMagFilter(SamplerFilterMode mode) {
if (mode == m_samplerParameters.magFilter) return;
m_samplerParameters.magFilter = mode;
++m_version;
}
void SamplerObject::SetMipmapMode(SamplerMipmapMode mode) {
if (mode == m_samplerParameters.mipmapMode) return;
m_samplerParameters.mipmapMode = mode;
++m_version;
}
void SamplerObject::SetLodRange(Float minLod, Float maxLod) {
if (minLod == m_samplerParameters.minLod && maxLod == m_samplerParameters.maxLod) return;
m_samplerParameters.minLod = minLod;
m_samplerParameters.maxLod = maxLod;
++m_version;
}
void SamplerObject::SetLodBias(Float bias) {
if (bias == m_samplerParameters.lodBias) return;
m_samplerParameters.lodBias = bias;
++m_version;
}
void SamplerObject::SetMaxAnisotropy(Float maxAnisotropy) {
if (maxAnisotropy == m_samplerParameters.maxAnisotropy) return;
m_samplerParameters.maxAnisotropy = maxAnisotropy;
++m_version;
}
void SamplerObject::SetSamplerCompareFunc(SamplerCompareFunc func) {
if (func == m_samplerParameters.compareFunc) return;
m_samplerParameters.compareFunc = func;
++m_version;
}
void SamplerObject::SetCompareMode(SamplerCompareMode mode) {
if (mode == m_samplerParameters.compareMode) return;
m_samplerParameters.compareMode = mode;
++m_version;
}
SamplerWrapMode SamplerObject::GetWrapS() const {
return m_samplerParameters.wrapS;
}
SamplerWrapMode SamplerObject::GetWrapT() const {
return m_samplerParameters.wrapT;
}
SamplerWrapMode SamplerObject::GetWrapR() const {
return m_samplerParameters.wrapR;
}
SamplerFilterMode SamplerObject::GetMinFilter() const {
return m_samplerParameters.minFilter;
}
SamplerFilterMode SamplerObject::GetMagFilter() const {
return m_samplerParameters.magFilter;
}
SamplerMipmapMode SamplerObject::GetMipmapMode() const {
return m_samplerParameters.mipmapMode;
}
Float SamplerObject::GetMinLod() const {
return m_samplerParameters.minLod;
}
Float SamplerObject::GetMaxLod() const {
return m_samplerParameters.maxLod;
}
Float SamplerObject::GetLodBias() const {
return m_samplerParameters.lodBias;
}
Float SamplerObject::GetMaxAnisotropy() const {
return m_samplerParameters.maxAnisotropy;
}
// The three border-colour representations are kept in step so a getter of any form has
// an answer whichever form was written. Integer <-> float uses the plain value, matching
// what glTexParameterIiv/Iuiv mean: those forms are for integer texture formats, whose
// border components are the raw integers rather than a normalized fraction.
void SamplerObject::SetBorderColor(const FloatVec4& color) {
if (color == m_samplerParameters.borderColor) return;
m_samplerParameters.borderColor = color;
m_samplerParameters.borderColorI =
IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
m_samplerParameters.borderColorUI =
UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
++m_version;
}
void SamplerObject::SetBorderColorI(const IntVec4& color) {
if (color == m_samplerParameters.borderColorI) return;
m_samplerParameters.borderColorI = color;
m_samplerParameters.borderColorUI =
UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
m_samplerParameters.borderColor =
FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
++m_version;
}
void SamplerObject::SetBorderColorUI(const UintVec4& color) {
if (color == m_samplerParameters.borderColorUI) return;
m_samplerParameters.borderColorUI = color;
m_samplerParameters.borderColorI =
IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
m_samplerParameters.borderColor =
FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
++m_version;
}
const FloatVec4& SamplerObject::GetBorderColor() const {
return m_samplerParameters.borderColor;
}
const IntVec4& SamplerObject::GetBorderColorI() const {
return m_samplerParameters.borderColorI;
}
const UintVec4& SamplerObject::GetBorderColorUI() const {
return m_samplerParameters.borderColorUI;
}
SamplerCompareMode SamplerObject::GetCompareMode() const {
return m_samplerParameters.compareMode;
}
SamplerCompareFunc SamplerObject::GetSamplerCompareFunc() const {
return m_samplerParameters.compareFunc;
}
Uint SamplerObject::GetExternalIndex() const {
return m_externalIndex;
}
const SamplerParameters& SamplerObject::GetAllSamplerParameters() const {
return m_samplerParameters;
}
Uint16 SamplerObject::GetVersion() const {
return m_version;
}
Uint64 SamplerObject::GetLifetimeId() const {
return m_lifetimeId;
}
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL