Files
MobileGL/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.cpp
T
BZLZHH 1f1a331a44 [Feat] (MG_Impl, MG_State): implement the framebuffer parameter getters and setters
glFramebufferParameteri, glGetFramebufferParameteriv and their two by-name
siblings were all export stubs - the GL_ARB_framebuffer_no_attachments entry
points. The stub raises no error and writes nothing, so
direct_state_access.framebuffers_get_parameter_errors saw GL_NO_ERROR for all
three conditions it checks.

FramebufferObject gains the five DEFAULT_* parameters as real state, initialised
to GL 4.6 core table 23.24 and bumping the object version on a write like the
read buffer does. The getter answers those plus the six derived names -
GL_SAMPLES and GL_SAMPLE_BUFFERS from the attachments' sample counts,
GL_IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE from the read buffer's internal format,
GL_DOUBLEBUFFER true only for the window-system framebuffer, GL_STEREO false
because stereo surfaces are not exposed - which is what glGetIntegerv already
reports for the bound framebuffer.

The pname rules live in ValidateFramebufferParameterPname, and their ORDER is
load-bearing: a name outside the table is INVALID_ENUM, and only a name that IS
in the table but that the default framebuffer cannot answer is INVALID_OPERATION.
Testing the framebuffer kind first would answer INVALID_ENUM for
GL_FRAMEBUFFER_DEFAULT_WIDTH on framebuffer zero, which is exactly the third
thing the case checks. The by-name forms take zero as the default framebuffer,
like the other DSA framebuffer entry points.

Rendering to a framebuffer with no attachments is deliberately NOT enabled by
this: CheckCompleteness still reports INCOMPLETE_MISSING_ATTACHMENT, because no
backend can rasterize one. The state is real and the queries are honest; the
draw path is a separate piece of work.

Takes framebuffers_get_parameter_errors from failing to passing on both backends,
with framebuffers_get_parameters - which passed only because both getters were
stubs leaving the CTS's zero-initialised comparands untouched - still passing.
2026-08-05 05:33:31 -04:00

207 lines
8.4 KiB
C++

// MobileGL - MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.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 "FramebufferObject.h"
#include "MG_Util/Types.h"
namespace MobileGL::MG_State::GLState {
// FramebufferAttachmentObject
FramebufferAttachmentObject::FramebufferAttachmentObject(
const SharedPtr<MG_State::GLState::ITextureObject>& texture, TextureUploadTarget textureUploadTarget, Int level,
Int layer, Bool layered)
: m_texture(texture), m_textureUploadTarget(textureUploadTarget), m_textureLevel(level),
m_textureLayer(layer), m_layered(layered) {}
FramebufferAttachmentObject::FramebufferAttachmentObject(const SharedPtr<RenderbufferObject>& renderbuffer)
: m_renderbuffer(renderbuffer) {}
FramebufferAttachmentObject::FramebufferAttachmentObject(Bool IsValid)
: m_texture(nullptr), m_renderbuffer(nullptr) {
m_isValid = IsValid;
}
Bool FramebufferAttachmentObject::IsTexture() const {
return m_texture != nullptr;
}
Bool FramebufferAttachmentObject::IsRenderbuffer() const {
return m_renderbuffer != nullptr;
}
Bool FramebufferAttachmentObject::IsEmpty() const {
return m_texture == nullptr && m_renderbuffer == nullptr;
}
const SharedPtr<MG_State::GLState::ITextureObject>& FramebufferAttachmentObject::GetTexture() const {
return m_texture;
}
const SharedPtr<RenderbufferObject>& FramebufferAttachmentObject::GetRenderbuffer() const {
return m_renderbuffer;
}
Int FramebufferAttachmentObject::GetTextureLevel() const {
return m_textureLevel;
}
Int FramebufferAttachmentObject::GetTextureLayer() const {
return m_textureLayer;
}
Bool FramebufferAttachmentObject::IsLayered() const {
return m_layered;
}
TextureUploadTarget FramebufferAttachmentObject::GetTextureUploadTarget() const {
return m_textureUploadTarget;
}
Bool FramebufferAttachmentObject::IsComplete() const {
if (IsTexture()) {
Bool complete = m_texture->IsComplete();
return complete;
} else if (IsRenderbuffer()) {
Bool complete = m_renderbuffer->IsAllocated();
return complete;
}
return false;
}
IntVec3 FramebufferAttachmentObject::GetSize() const {
if (IsTexture()) {
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get()),
"Texture object here should always be an object with mipmap");
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get());
TextureUploadTarget resolvedTarget = m_textureUploadTarget;
if (resolvedTarget == TextureUploadTarget::Unknown) {
const auto& uploadTargets = m_texture->GetUploadTargets();
MOBILEGL_ASSERT(!uploadTargets.empty(),
"FramebufferAttachmentObject::GetSize: textureId=%u exposes no upload targets",
m_texture->GetExternalIndex());
resolvedTarget = uploadTargets[0];
}
return textureMipmapObject->GetMipmapTexelSize(resolvedTarget, m_textureLevel);
} else if (IsRenderbuffer()) {
return {m_renderbuffer->GetWidth(), m_renderbuffer->GetHeight(), 1};
}
return {0, 0, 0};
}
Bool FramebufferAttachmentObject::IsValid() const {
return m_isValid;
}
// FramebufferObject
FramebufferObject::FramebufferObject(Uint externalIndex)
: m_externalIndex(externalIndex), m_attachmentVersions{}, m_drawBuffers{} {
m_attachmentObjects.fill(FramebufferAttachmentObject(false));
m_drawBuffers.fill(FramebufferAttachmentType::None);
const FramebufferAttachmentType defaultColorBuffer =
(externalIndex == 0) ? FramebufferAttachmentType::BackLeft : FramebufferAttachmentType::Color0;
m_drawBuffers[0] = defaultColorBuffer;
m_readBuffer = defaultColorBuffer;
m_attachmentVersions.fill(0);
}
void FramebufferObject::AttachTexture(FramebufferAttachmentType type, const SharedPtr<ITextureObject>& texture,
TextureUploadTarget textureUploadTarget, int level, int layer, Bool layered) {
m_attachmentObjects[static_cast<SizeT>(type)] =
FramebufferAttachmentObject(texture, textureUploadTarget, level, layer, layered);
BumpAttachmentVersion(type);
}
void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type,
const SharedPtr<RenderbufferObject>& renderbuffer) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(renderbuffer);
BumpAttachmentVersion(type);
}
void FramebufferObject::Detach(FramebufferAttachmentType type) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(false);
BumpAttachmentVersion(type);
}
const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const {
return m_attachmentObjects[static_cast<SizeT>(type)];
}
const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachmentObjects() const {
return m_attachmentObjects;
}
Bool FramebufferObject::CheckCompleteness() const {
if (m_attachmentObjects.empty()) {
return false;
}
Int width = -1, height = -1;
Int validAttachmentCount = 0;
for (const auto& attachmentObject : m_attachmentObjects) {
if (!attachmentObject.IsValid()) continue;
++validAttachmentCount;
const auto& attachment = attachmentObject;
auto attachmentSize = attachment.GetSize();
Int w = attachmentSize.x();
Int h = attachmentSize.y();
if (width == -1) {
width = w;
height = h;
} else if (width != w || height != h) {
return false;
}
if (!attachment.IsComplete()) {
return false;
}
}
if (validAttachmentCount == 0) return false;
return true;
}
void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) {
if (m_drawBuffers[index] == buffer) return;
m_drawBuffers[index] = buffer;
BumpAttachmentVersion(buffer);
}
const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const {
return m_drawBuffers;
}
void FramebufferObject::SetReadBuffer(FramebufferAttachmentType buf) {
if (m_readBuffer == buf) return;
m_readBuffer = buf;
++m_objectVersion;
}
Uint FramebufferObject::GetExternalIndex() const {
return m_externalIndex;
}
#define MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(name, member, type) \
void FramebufferObject::Set##name(type value) { \
if (member == value) return; \
member = value; \
++m_objectVersion; \
}
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultWidth, m_defaultWidth, Int)
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultHeight, m_defaultHeight, Int)
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultLayers, m_defaultLayers, Int)
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultSamples, m_defaultSamples, Int)
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultFixedSampleLocations, m_defaultFixedSampleLocations, Bool)
#undef MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER
void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) {
++m_attachmentVersions[static_cast<SizeT>(type)];
++m_objectVersion;
}
} // namespace MobileGL::MG_State::GLState