Files
MobileGL/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp
T

777 lines
38 KiB
C++

// MobileGL - MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.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 "GL_Framebuffer.h"
#include "Validators.h"
#include "Config.h"
#include <MG_Util/Metrics/TextureMetrics.h>
#include <MG_Impl/GLImpl/Texture/Validators.h>
#include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
#include <MG_Util/Converters/GLToMG/FramebufferEnumConverter.h>
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
#include <MG_Backend/DirectGLES/DirectGLES.h>
#endif
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
#include <MG_Backend/DirectVulkan/TmpImpl.h>
#endif
namespace MobileGL {
namespace MG_Impl::GLImpl {
void BlitFramebuffer_Backend(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask,
filter);
#elif MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
MG_Backend::DirectVulkan::TmpImpl::BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
mask, filter);
#endif
}
void SampleMaski_State(GLuint maskNumber, GLbitfield mask) {
// TODO: implement
}
void RenderbufferStorageMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
GLsizei height) {
// TODO: implement
}
void RenderbufferStorage_State(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget);
auto renderbufferObject = bindingSlot.GetBoundObject();
if (!renderbufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State",
"Renderbuffer target is bound to no renderbuffer object."));
return;
}
TextureInternalFormat format = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
if (!TextureImpl::ValidateTextureInternalFormat(format)) return;
if (width < 0 || height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State",
"Width and height must be non-negative."));
return;
}
renderbufferObject->AllocateStorage({width, height});
renderbufferObject->SetInternalFormat(format);
}
GLboolean IsRenderbuffer_State(GLuint renderbuffer) {
return MG_State::pGLContext->ValidateRenderbufferName(renderbuffer);
}
GLboolean IsFramebuffer_State(GLuint framebuffer) {
return MG_State::pGLContext->ValidateFramebufferName(framebuffer);
}
void GetFramebufferAttachmentParameteriv_State(GLenum target, GLenum attachment, GLenum pname, GLint* params) {
// TODO: implement
}
void GenerateMipmap_Backend(GLenum target) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::GenerateMipmap(target);
#endif
}
void GenRenderbuffers_State(GLsizei n, GLuint* renderbuffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenRenderbuffers_State", "n must be non-negative"));
return;
}
auto renderbufferNames = MG_State::pGLContext->GenRenderbufferNames(n);
Memcpy(renderbuffers, renderbufferNames.data(), sizeof(GLuint) * static_cast<SizeT>(n));
}
void GenFramebuffers_State(GLsizei n, GLuint* framebuffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenFramebuffers_State", "n must be non-negative"));
return;
}
auto framebuffersNames = MG_State::pGLContext->GenFramebufferNames(n);
Memcpy(framebuffers, framebuffersNames.data(), sizeof(GLuint) * static_cast<SizeT>(n));
}
void FramebufferTextureLayer_State(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
// TODO: implement
}
void FramebufferTexture3D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
GLint zoffset) {
// TODO: implement
}
void FramebufferTexture2D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level) {
if (target == GL_FRAMEBUFFER) {
target = GL_DRAW_FRAMEBUFFER;
}
if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
FramebufferTexture2D_State(target, GL_DEPTH_ATTACHMENT, textarget, texture, level);
FramebufferTexture2D_State(target, GL_STENCIL_ATTACHMENT, textarget, texture, level);
}
FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment);
FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return;
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
if (!TextureImpl::ValidateTextureName(texture, true)) return;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State",
"Framebuffer target is bound to no framebuffer object."));
return;
}
if (texture == 0) {
framebufferObject->Detach(attachmentType);
return;
}
auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
if (!textureObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State",
std::format("Texture object {} is not valid.", texture)));
return;
}
framebufferObject->AttachTexture(attachmentType, textureObject, textureObject ? level : 0);
}
void FramebufferTexture1D_State(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level) {
// TODO: implement
}
void FramebufferTexture_State(GLenum target, GLenum attachment, GLuint texture, GLint level) {
// TODO: implement
}
void FramebufferRenderbuffer_State(GLenum target, GLenum attachment, GLenum renderbuffertarget,
GLuint renderbuffer) {
if (target == GL_FRAMEBUFFER) {
target = GL_DRAW_FRAMEBUFFER;
}
FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment);
FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(renderbuffertarget);
if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return;
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State",
"Framebuffer target is bound to no framebuffer object."));
return;
}
if (renderbuffer == 0) {
framebufferObject->Detach(attachmentType);
return;
}
auto renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
if (!renderbufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State",
std::format("Renderbuffer object {} is not valid.", renderbuffer)));
return;
}
framebufferObject->AttachRenderbuffer(attachmentType, renderbufferObject);
}
void DrawBuffers_State(GLsizei n, const GLenum* bufs) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`n` is less than 0."));
return;
} else if (n > MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"`n` is greater than `GL_MAX_DRAW_BUFFERS`."));
return;
}
// Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
auto fbo = bindingSlot.GetBoundObject();
bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
static int existenceMap[(SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount] = {-1};
std::fill(existenceMap, existenceMap + (SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount,
-1);
for (GLsizei i = 0; i < n; ++i) {
auto attType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(bufs[i]);
// ------------------- Check validity begin ------------------------
if (attType == FramebufferAttachmentType::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("bufs[{}] = {} is not an accepted value.", i,
MG_Util::ConvertGLEnumToString(bufs[i]))));
return;
}
if (isDefaultFBO && attType >= FramebufferAttachmentType::Color0 &&
attType <= FramebufferAttachmentType::Color31) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
std::format(
"FBO is default FBO, but bufs[{}] = {} is one of the `GL_COLOR_ATTACHMENTn` tokens.", i,
MG_Util::ConvertGLEnumToString(bufs[i]))));
return;
}
if (!isDefaultFBO && attType >= FramebufferAttachmentType::FrontLeft &&
attType <= FramebufferAttachmentType::BackRight) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
std::format("FBO is not default FBO, but bufs[{}] = {} is anything other than `GL_NONE` or "
"one of the `GL_COLOR_ATTACHMENTn` tokens.",
i, MG_Util::ConvertGLEnumToString(bufs[i]))));
return;
}
if (attType != FramebufferAttachmentType::None && existenceMap[(SizeT)attType] >= 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("a symbolic constant other than `GL_NONE` appears "
"more than once in bufs. bufs[{}] == bufs[{}] == {}.",
i, existenceMap[(SizeT)attType],
MG_Util::ConvertGLEnumToString(bufs[i]))));
return;
}
existenceMap[(SizeT)attType] = i;
if ((SizeT)attType >
(SizeT)FramebufferAttachmentType::Color0 + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("bufs[{}] == {} indicates a color buffer that does "
"not exist in the current GL context.",
i, MG_Util::ConvertGLEnumToString(bufs[i]))));
return;
}
// ------------------------- Check validity end ----------------------------------
fbo->SetDrawBuffer(i, attType);
}
for (GLsizei i = n; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
fbo->SetDrawBuffer(i, FramebufferAttachmentType::None);
}
}
void DrawBuffer_State(GLenum buf) {
if (buf == GL_NONE) {
DrawBuffers_State(0, nullptr);
} else {
static GLenum bufs[] = {buf};
DrawBuffers_State(1, bufs);
}
}
void ReadBuffer_State(GLenum mode) {
auto attType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(mode);
// ------------------- Check validity begin ------------------------
if (attType == FramebufferAttachmentType::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
std::format("`mode` = {} is not an accepted value.", MG_Util::ConvertGLEnumToString(mode))));
return;
}
// Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto fbo = bindingSlot.GetBoundObject();
fbo->SetReadBuffer(attType);
}
void DeleteRenderbuffers_State(GLsizei n, const GLuint* renderbuffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State",
"n must be non-negative."));
return;
}
if (!renderbuffers) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State",
"Renderbuffer names array cannot be null."));
return;
}
for (SizeT i = 0; i < static_cast<SizeT>(n); ++i) {
Uint bufferName = renderbuffers[i];
if (bufferName == 0) continue;
if (!FramebufferImpl::ValidateRenderbufferName(bufferName)) continue;
MG_State::pGLContext->MarkRenderbufferObjectForDeletion(bufferName);
}
}
void DeleteFramebuffers_State(GLsizei n, const GLuint* framebuffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State",
"n must be non-negative."));
return;
}
if (!framebuffers) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State",
"Framebuffer names array cannot be null."));
return;
}
for (SizeT i = 0; i < static_cast<SizeT>(n); ++i) {
Uint bufferName = framebuffers[i];
if (bufferName == 0) continue;
if (!FramebufferImpl::ValidateFramebufferName(bufferName)) continue;
MG_State::pGLContext->MarkFramebufferObjectForDeletion(bufferName);
}
}
GLenum CheckFramebufferStatus_State(GLenum target) {
FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return GL_FRAMEBUFFER_UNDEFINED;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CheckFramebufferStatus_State",
"Framebuffer target is bound to no framebuffer object."));
return GL_FRAMEBUFFER_UNDEFINED;
}
// TODO: distinguish GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT and GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
// TODO: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
// GL_FRAMEBUFFER_UNSUPPORTED, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
// GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS
return framebufferObject->CheckCompleteness() ? GL_FRAMEBUFFER_COMPLETE
: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
}
void BindRenderbuffer_State(GLenum target, GLuint renderbuffer) {
if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return;
RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return;
auto renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
if (!renderbufferObject) {
MG_State::pGLContext->CreateRenderbufferObject(renderbuffer);
renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
}
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget);
bindingSlot.Bind(renderbufferObject);
}
void BindFramebuffer_State(GLenum target, GLuint framebuffer) {
if (target == GL_FRAMEBUFFER) {
BindFramebuffer_State(GL_DRAW_FRAMEBUFFER, framebuffer);
BindFramebuffer_State(GL_READ_FRAMEBUFFER, framebuffer);
return;
}
if (!FramebufferImpl::ValidateFramebufferName(framebuffer)) return;
FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer);
if (!framebufferObject) {
MG_State::pGLContext->CreateFramebufferObject(framebuffer);
framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer);
}
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
bindingSlot.Bind(framebufferObject);
}
void GetRenderbufferParameteriv_State(GLenum target, GLenum pname, GLint* params) {
if (!params) return;
RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget);
auto renderbufferObject = bindingSlot.GetBoundObject();
if (!renderbufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GetRenderbufferParameteriv_State",
"Renderbuffer target is bound to no renderbuffer object."));
return;
}
switch (pname) {
case GL_RENDERBUFFER_WIDTH:
*params = static_cast<GLint>(renderbufferObject->GetWidth());
break;
case GL_RENDERBUFFER_HEIGHT:
*params = static_cast<GLint>(renderbufferObject->GetHeight());
break;
case GL_RENDERBUFFER_INTERNAL_FORMAT:
*params = MG_Util::ConvertTextureInternalFormatToGLEnum(renderbufferObject->GetInternalFormat());
break;
case GL_RENDERBUFFER_RED_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetRedSize());
break;
case GL_RENDERBUFFER_GREEN_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetGreenSize());
break;
case GL_RENDERBUFFER_BLUE_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetBlueSize());
break;
case GL_RENDERBUFFER_ALPHA_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetAlphaSize());
break;
case GL_RENDERBUFFER_DEPTH_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetDepthSize());
break;
case GL_RENDERBUFFER_STENCIL_SIZE:
*params = static_cast<GLint>(renderbufferObject->GetStencilSize());
break;
case GL_RENDERBUFFER_SAMPLES:
*params = static_cast<GLint>(renderbufferObject->GetSamples());
break;
default:
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", "GetRenderbufferParameteriv_State",
std::format("pname {} is not an accepted value.", MG_Util::ConvertGLEnumToString(pname))));
return;
}
}
void ClearBufferfi_Backend(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::ClearBufferfi(buffer, drawbuffer, depth, stencil);
#endif
}
void ClearBufferfv_Backend(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::ClearBufferfv(buffer, drawbuffer, value);
#endif
}
void ClearBufferuiv_Backend(GLenum buffer, GLint drawbuffer, const GLuint* value) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::ClearBufferuiv(buffer, drawbuffer, value);
#endif
}
void ClearBufferiv_Backend(GLenum buffer, GLint drawbuffer, const GLint* value) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::ClearBufferiv(buffer, drawbuffer, value);
#endif
}
void ReadPixels_State(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels) {
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
// Check width/height
if (width < 0 || height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Width and height must be non-negative"));
return;
}
// Validate format
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid format"));
return;
}
// Validate type
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid pixel data type"));
return;
}
// Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No framebuffer bound to read target"));
return;
}
// Check framebuffer completeness
if (!framebufferObject->CheckCompleteness()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidFramebufferOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete"));
return;
}
// Check for required buffers
if (textureInputFormat == TextureInputFormat::StencilIndex) {
if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No stencil buffer for stencil index format"));
return;
}
} else if (textureInputFormat == TextureInputFormat::DepthComponent) {
if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No depth buffer for depth component format"));
return;
}
} else if (textureInputFormat == TextureInputFormat::DepthStencil) {
if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid() ||
!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No depth/stencil buffer for depth-stencil format"));
return;
}
// Validate type for depth/stencil
if (texturePixelDataType != TexturePixelDataType::UnsignedInt248 &&
texturePixelDataType != TexturePixelDataType::Float32UnsignedInt248Rev) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Invalid type for depth-stencil format"));
return;
}
}
// Check PBO state
const auto& pixelPackBufferObject =
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
if (pixelPackBufferObject) {
// Check if PBO is mapped
if (pixelPackBufferObject->IsMapped()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Pixel pack buffer is currently mapped"));
return;
}
// Check alignment
const SizeT typeSize = MG_Util::GetTexturePixelDataTypeSize(texturePixelDataType);
if (reinterpret_cast<uintptr_t>(pixels) % typeSize != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Pixel data not aligned for pixel pack buffer"));
return;
}
}
// Check multisampling
if (framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).IsRenderbuffer()) {
auto rbo = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).GetRenderbuffer();
if (rbo && rbo->GetSamples() > 1) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"ReadPixels not supported for multisampled framebuffers"));
return;
}
}
}
void ReadPixels_Backend(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels) {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::ReadPixels(x, y, width, height, format, type, pixels);
#endif
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
ReadPixels_State(x, y, width, height, format, type, pixels);
ReadPixels_Backend(x, y, width, height, format, type, pixels);
}
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
ClearBufferfi_Backend(buffer, drawbuffer, depth, stencil);
}
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
ClearBufferfv_Backend(buffer, drawbuffer, value);
}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
ClearBufferuiv_Backend(buffer, drawbuffer, value);
}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
ClearBufferiv_Backend(buffer, drawbuffer, value);
}
void SampleMaski(GLuint maskNumber, GLbitfield mask) {
SampleMaski_State(maskNumber, mask);
}
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
GLsizei height) {
RenderbufferStorageMultisample_State(target, samples, internalformat, width, height);
}
void RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
RenderbufferStorage_State(target, internalformat, width, height);
}
GLboolean IsRenderbuffer(GLuint renderbuffer) {
return IsRenderbuffer_State(renderbuffer);
}
GLboolean IsFramebuffer(GLuint framebuffer) {
return IsFramebuffer_State(framebuffer);
}
void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) {
GetFramebufferAttachmentParameteriv_State(target, attachment, pname, params);
}
void GenerateMipmap(GLenum target) {
GenerateMipmap_Backend(target);
}
void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) {
GenRenderbuffers_State(n, renderbuffers);
}
void GenFramebuffers(GLsizei n, GLuint* framebuffers) {
GenFramebuffers_State(n, framebuffers);
}
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
FramebufferTextureLayer_State(target, attachment, texture, level, layer);
}
void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
GLint zoffset) {
FramebufferTexture3D_State(target, attachment, textarget, texture, level, zoffset);
}
void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
FramebufferTexture2D_State(target, attachment, textarget, texture, level);
}
void FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
FramebufferTexture1D_State(target, attachment, textarget, texture, level);
}
void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level) {
FramebufferTexture_State(target, attachment, texture, level);
}
void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
FramebufferRenderbuffer_State(target, attachment, renderbuffertarget, renderbuffer);
}
void DrawBuffer(GLenum buf) {
DrawBuffer_State(buf);
}
void DrawBuffers(GLsizei n, const GLenum* bufs) {
DrawBuffers_State(n, bufs);
}
void ReadBuffer(GLenum src) {
ReadBuffer_State(src);
}
void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) {
DeleteRenderbuffers_State(n, renderbuffers);
}
void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {
DeleteFramebuffers_State(n, framebuffers);
}
GLenum CheckFramebufferStatus(GLenum target) {
return CheckFramebufferStatus_State(target);
}
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter) {
BlitFramebuffer_Backend(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
void BindRenderbuffer(GLenum target, GLuint renderbuffer) {
BindRenderbuffer_State(target, renderbuffer);
}
void BindFramebuffer(GLenum target, GLuint framebuffer) {
BindFramebuffer_State(target, framebuffer);
}
void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) {
GetRenderbufferParameteriv_State(target, pname, params);
}
namespace FramebufferImpl {
DefaultFramebufferInfo* pDefaultFramebufferInfo;
}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL