mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
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.
207 lines
8.4 KiB
C++
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
|