From e644efbe815e8fd413d1d6d2ded92008e287f148 Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Wed, 30 Apr 2025 17:31:10 +0800 Subject: [PATCH] [Feat|Improvement] (...): (MG_State) Implement glPixelStorei. Optimize code. --- CMakeLists.txt | 2 + MG/Constants.h | 30 ++ MG/Includes.h | 3 + MG/MG_GL/GL/Common/GL_Common.cpp | 9 + MG/MG_GL/GL/Common/GL_Common.h | 1 + MG/MG_GL/GL_Def/GL_Def.cpp | 2 +- MG/MG_GL/State/Common/CommonState.cpp | 56 +++ MG/MG_GL/State/Common/CommonState.h | 20 ++ MG/MG_GL/State/Core/GLState.cpp | 14 +- MG/MG_GL/State/Core/GLState.h | 4 + MG/MG_GL/State/Texture/TextureState.cpp | 456 ++++++++++++++++++------ MG/MG_GL/State/Texture/TextureState.h | 17 +- 12 files changed, 507 insertions(+), 107 deletions(-) create mode 100644 MG/MG_GL/State/Common/CommonState.cpp create mode 100644 MG/MG_GL/State/Common/CommonState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da2e4e83..0a9518b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED # MG_GL/State MG/MG_GL/State/Core/GLState.cpp + MG/MG_GL/State/Common/CommonState.cpp + MG/MG_GL/State/Texture/TextureState.cpp # MG_GL/GLX diff --git a/MG/Constants.h b/MG/Constants.h index 2e42fe94..f184497f 100644 --- a/MG/Constants.h +++ b/MG/Constants.h @@ -22,6 +22,36 @@ namespace MG_Constants { inline const int LOG_LEVEL_INFO = 0x0013; inline const int LOG_LEVEL_FATAL = 0x0014; } + + namespace PixelStore { + static const std::unordered_map DEFAULT_VALUES_MAP = { + {GL_PACK_SWAP_BYTES, GL_FALSE}, + {GL_PACK_LSB_FIRST, GL_FALSE}, + {GL_PACK_ROW_LENGTH, 0}, + {GL_PACK_IMAGE_HEIGHT, 0}, + {GL_PACK_SKIP_ROWS, 0}, + {GL_PACK_SKIP_PIXELS, 0}, + {GL_PACK_SKIP_IMAGES, 0}, + {GL_PACK_ALIGNMENT, 4}, + {GL_UNPACK_SWAP_BYTES, GL_FALSE}, + {GL_UNPACK_LSB_FIRST, GL_FALSE}, + {GL_UNPACK_ROW_LENGTH, 0}, + {GL_UNPACK_IMAGE_HEIGHT, 0}, + {GL_UNPACK_SKIP_ROWS, 0}, + {GL_UNPACK_SKIP_PIXELS,0}, + {GL_UNPACK_SKIP_IMAGES,0}, + {GL_UNPACK_ALIGNMENT, 4} + }; // OpenGL 3 + + static const std::unordered_set VALID_PARAM_NAMES = { + GL_PACK_SWAP_BYTES, GL_PACK_LSB_FIRST, GL_PACK_ROW_LENGTH, GL_PACK_IMAGE_HEIGHT, + GL_PACK_SKIP_ROWS, GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_IMAGES, GL_PACK_ALIGNMENT, + GL_UNPACK_SWAP_BYTES, GL_UNPACK_LSB_FIRST, GL_UNPACK_ROW_LENGTH, + GL_UNPACK_IMAGE_HEIGHT, + GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_IMAGES, + GL_UNPACK_ALIGNMENT + }; // OpenGL 3 + } namespace Texture { inline const GLuint MAX_TEXTURE_UNITS = 80; diff --git a/MG/Includes.h b/MG/Includes.h index aaedba06..86fdb97a 100644 --- a/MG/Includes.h +++ b/MG/Includes.h @@ -38,6 +38,9 @@ #ifndef MOBILEGL_GLSTATE_H #include "MG_GL/State/Core/GLState.h" #endif +#ifndef MOBILEGL_COMMONSTATE_H +#include "MG_GL/State/Common/CommonState.h" +#endif #ifndef MOBILEGL_TEXTURESTATE_H #include "MG_GL/State/Texture/TextureState.h" #endif diff --git a/MG/MG_GL/GL/Common/GL_Common.cpp b/MG/MG_GL/GL/Common/GL_Common.cpp index 7474d737..24a98644 100644 --- a/MG/MG_GL/GL/Common/GL_Common.cpp +++ b/MG/MG_GL/GL/Common/GL_Common.cpp @@ -16,4 +16,13 @@ namespace MG_GL::GL { void Clear(GLbitfield mask) { } + + void PixelStorei(GLenum pname, GLint param) { + MG_Util::Debug::LogD("glPixelStorei, pname: %d, param: %d", pname, param); + GLenum result = MG_State::SetPixelStoreInt(pname,param); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } } \ No newline at end of file diff --git a/MG/MG_GL/GL/Common/GL_Common.h b/MG/MG_GL/GL/Common/GL_Common.h index 0eed4b80..f2e130ae 100644 --- a/MG/MG_GL/GL/Common/GL_Common.h +++ b/MG/MG_GL/GL/Common/GL_Common.h @@ -11,6 +11,7 @@ namespace MG_GL::GL { void ClearDepth(::GLdouble depth); void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); void Clear(GLbitfield mask); + void PixelStorei(GLenum pname, GLint param); } #endif //MOBILEGL_GL_COMMON_H diff --git a/MG/MG_GL/GL_Def/GL_Def.cpp b/MG/MG_GL/GL_Def/GL_Def.cpp index 8349ad4c..7a94368c 100644 --- a/MG/MG_GL/GL_Def/GL_Def.cpp +++ b/MG/MG_GL/GL_Def/GL_Def.cpp @@ -122,7 +122,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsShader, GLuint shader) DECLARE_GL_FUN DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsTexture, GLuint texture) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsTexture, texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, LineWidth, GLfloat width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, LineWidth, width) DECLARE_GL_FUNCTION_STUB_HEAD(void, LinkProgram, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, LinkProgram, program) -DECLARE_GL_FUNCTION_STUB_HEAD(void, PixelStorei, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PixelStorei, pname,param) +DECLARE_GL_FUNCTION_HEAD(void, PixelStorei, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PixelStorei, pname,param) DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffset, GLfloat factor, GLfloat units) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffset, factor,units) DECLARE_GL_FUNCTION_STUB_HEAD(void, ReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReadPixels, x,y,width,height,format,type,pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, ReleaseShaderCompiler) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReleaseShaderCompiler) diff --git a/MG/MG_GL/State/Common/CommonState.cpp b/MG/MG_GL/State/Common/CommonState.cpp new file mode 100644 index 00000000..c08f30e5 --- /dev/null +++ b/MG/MG_GL/State/Common/CommonState.cpp @@ -0,0 +1,56 @@ +// +// Created by BZLZHH on 2025/4/30. +// + +#include "CommonState.h" + +GLenum CommonState::SetPixelStoreInt(GLenum pname, GLint param) { + if (MG_Constants::PixelStore::VALID_PARAM_NAMES.find(pname) == MG_Constants::PixelStore::VALID_PARAM_NAMES.end()) { + return GL_INVALID_ENUM; + } + + switch (pname) { + case GL_PACK_SWAP_BYTES: + case GL_PACK_LSB_FIRST: + case GL_UNPACK_SWAP_BYTES: + case GL_UNPACK_LSB_FIRST: + break; + case GL_PACK_ROW_LENGTH: + case GL_PACK_IMAGE_HEIGHT: + case GL_PACK_SKIP_ROWS: + case GL_PACK_SKIP_PIXELS: + case GL_PACK_SKIP_IMAGES: + case GL_UNPACK_ROW_LENGTH: + case GL_UNPACK_IMAGE_HEIGHT: + case GL_UNPACK_SKIP_ROWS: + case GL_UNPACK_SKIP_PIXELS: + case GL_UNPACK_SKIP_IMAGES: + if (param < 0) { + return GL_INVALID_VALUE; + } + break; + + case GL_PACK_ALIGNMENT: + case GL_UNPACK_ALIGNMENT: + if (param != 1 && param != 2 && param != 4 && param != 8) { + return GL_INVALID_VALUE; + } + break; + + default: + return GL_INVALID_ENUM; + } + + pixelStoreParams[pname] = param; + return GL_NO_ERROR; +} + +GLint CommonState::GetPixelStoreInt(GLenum pname) { + auto it = pixelStoreParams.find(pname); + if (it != pixelStoreParams.end()) { + return it->second; + } + + auto defaultIt = MG_Constants::PixelStore::DEFAULT_VALUES_MAP.find(pname); + return (defaultIt != MG_Constants::PixelStore::DEFAULT_VALUES_MAP.end()) ? defaultIt->second : 0; +} diff --git a/MG/MG_GL/State/Common/CommonState.h b/MG/MG_GL/State/Common/CommonState.h new file mode 100644 index 00000000..efa74859 --- /dev/null +++ b/MG/MG_GL/State/Common/CommonState.h @@ -0,0 +1,20 @@ +// +// Created by BZLZHH on 2025/4/30. +// + +#ifndef MOBILEGL_COMMONSTATE_H +#define MOBILEGL_COMMONSTATE_H +#define MOBILEGL_GLSTATE_H + +#include "../../../Includes.h" + +class CommonState { +private: + std::unordered_map pixelStoreParams; + +public: + GLenum SetPixelStoreInt(GLenum pname, GLint param); + GLint GetPixelStoreInt(GLenum pname); +}; + +#endif //MOBILEGL_COMMONSTATE_H diff --git a/MG/MG_GL/State/Core/GLState.cpp b/MG/MG_GL/State/Core/GLState.cpp index 6419a9dc..60096e58 100644 --- a/MG/MG_GL/State/Core/GLState.cpp +++ b/MG/MG_GL/State/Core/GLState.cpp @@ -4,16 +4,19 @@ #include "GLState.h" -TextureState* MG_State_T::textureState; std::queue MG_State_T::glErrorQueue; +TextureState* MG_State_T::textureState = nullptr; +CommonState* MG_State_T::commonState = nullptr; namespace MG_State { void Init() { MG_State_T::textureState = new TextureState(); + MG_State_T::commonState = new CommonState(); } void Destroy() { delete MG_State_T::textureState; + delete MG_State_T::commonState; } void SetError(GLenum error) { @@ -80,4 +83,13 @@ namespace MG_State { GLenum GetTextureLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params) { return MG_State_T::textureState->GetLevelPropertyIntVector(target, level, pname, params); } + + GLenum MG_State::SetPixelStoreInt(GLenum pname, GLint param) { + return MG_State_T::commonState->SetPixelStoreInt(pname, param); + } + + GLint MG_State::GetPixelStoreInt(GLenum pname) { + return MG_State_T::commonState->GetPixelStoreInt(pname); + } + } \ No newline at end of file diff --git a/MG/MG_GL/State/Core/GLState.h b/MG/MG_GL/State/Core/GLState.h index e68fd07b..26e1ec15 100644 --- a/MG/MG_GL/State/Core/GLState.h +++ b/MG/MG_GL/State/Core/GLState.h @@ -10,6 +10,7 @@ struct MG_State_T { static std::queue glErrorQueue; static TextureState* textureState; + static CommonState* commonState; }; namespace MG_State { @@ -36,6 +37,9 @@ namespace MG_State { GLenum DeleteTexture(GLuint texture); GLenum DeleteTextures(GLsizei n, const GLuint* textures); GLenum GetTextureLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params); + GLenum SetPixelStoreInt(GLenum pname, GLint param); + + GLint GetPixelStoreInt(GLenum pname); } diff --git a/MG/MG_GL/State/Texture/TextureState.cpp b/MG/MG_GL/State/Texture/TextureState.cpp index 30de1f8d..6c2b0c21 100644 --- a/MG/MG_GL/State/Texture/TextureState.cpp +++ b/MG/MG_GL/State/Texture/TextureState.cpp @@ -4,6 +4,10 @@ #include "TextureState.h" +// Re-Include Includes.h, cuz of the absence of GLState.h +#undef MOBILEGL_GLSTATE_H +#include "../../../Includes.h" + // TextureObject bool TextureObject::IsImmutable() const { @@ -33,10 +37,10 @@ TextureState::TextureState() { MG_Util::Debug::LogD("MG_State: Texture: TextureState constructor, textureUnits=%zu", textureUnits.size()); } -TextureState &TextureState::GetInstance() { - static TextureState instance; - MG_Util::Debug::LogD("MG_State: Texture: GetInstance called"); - return instance; +GLint TextureState::GetUnpackParam(GLenum pname) { + GLint result = MG_State::GetPixelStoreInt(pname); + MG_Util::Debug::LogD("MG_State: Texture: GetUnpackParam pname=0x%x, returns %d", pname, result); + return result; } bool TextureState::IsTextureGenerated(GLuint texture) { @@ -172,6 +176,12 @@ GLenum TextureState::Bind(GLenum target, GLuint texture) { size_t TextureState::CalculatePixelDataSize(GLenum format, GLenum type, GLsizei width, GLsizei height) { MG_Util::Debug::LogD("MG_State: Texture: CalculatePixelDataSize called format=0x%x, type=0x%x, width=%d, height=%d", format, type, width, height); + + // Unpack Param + GLint rowLength = TextureState::GetUnpackParam(GL_UNPACK_ROW_LENGTH); + GLint alignment = TextureState::GetUnpackParam(GL_UNPACK_ALIGNMENT); + GLsizei actualRowLength = (rowLength > 0) ? rowLength : width; + size_t bytesPerPixel = 0; switch (type) { case GL_UNSIGNED_BYTE_3_3_2: @@ -219,7 +229,11 @@ size_t TextureState::CalculatePixelDataSize(GLenum format, GLenum type, GLsizei break; } } - size_t totalSize = static_cast(width) * static_cast(height) * bytesPerPixel; + + size_t rowSize = actualRowLength * bytesPerPixel; + size_t alignedRowSize = (rowSize + (alignment - 1)) & ~(alignment - 1); + + size_t totalSize = alignedRowSize * height; MG_Util::Debug::LogD("MG_State: Texture: CalculatePixelDataSize returns %zu", totalSize); return totalSize; } @@ -227,13 +241,15 @@ size_t TextureState::CalculatePixelDataSize(GLenum format, GLenum type, GLsizei GLenum TextureState::Upload2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data) { - MG_Util::Debug::LogD("MG_State: Texture: Upload2D called target=0x%x, level=%d, width=%d, height=%d", target, level, width, height); - GLenum validity = CheckUploadingTexture2DValidity(target, level, internalFormat, width, height, border, format, type, data); + MG_Util::Debug::LogD("MG_State: Texture: Upload2D called target=0x%x, level=%d, width=%d, height=%d", + target, level, width, height); + GLenum validity = CheckUploadingTexture2DValidity(target, level, internalFormat, + width, height, border, format, type, data); if (validity != GL_NO_ERROR) { - MG_Util::Debug::LogE("MG_State: Texture: Upload2D CheckUploadingTexture2DValidity failed with error 0x%x", validity); + MG_Util::Debug::LogE("MG_State: Texture: Upload2D validation failed with error 0x%x", validity); return validity; } - + bool isProxyTexture = (target == GL_PROXY_TEXTURE_1D || target == GL_PROXY_TEXTURE_2D || target == GL_PROXY_TEXTURE_3D || target == GL_PROXY_TEXTURE_CUBE_MAP || target == GL_PROXY_TEXTURE_1D_ARRAY || target == GL_PROXY_TEXTURE_2D_ARRAY || @@ -253,7 +269,7 @@ GLenum TextureState::Upload2D(GLenum target, GLint level, GLint internalFormat, proxyTex.params.mipmapData[level] = mip; return GL_NO_ERROR; } - + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); TextureObject& tex = textures[boundTex]; TextureParams::MipmapLevel mip{}; @@ -263,19 +279,56 @@ GLenum TextureState::Upload2D(GLenum target, GLint level, GLint internalFormat, mip.format = format; mip.type = type; if (data != nullptr) { - size_t dataSize = CalculatePixelDataSize(format, type, width, height); - mip.pixelData.resize(dataSize); - memcpy(mip.pixelData.data(), data, dataSize); + GLint unpackSwapBytes = GetUnpackParam(GL_UNPACK_SWAP_BYTES); + GLint unpackLSBFirst = GetUnpackParam(GL_UNPACK_LSB_FIRST); + GLint unpackSkipPixels = GetUnpackParam(GL_UNPACK_SKIP_PIXELS); + GLint unpackSkipRows = GetUnpackParam(GL_UNPACK_SKIP_ROWS); + GLint unpackRowLength = GetUnpackParam(GL_UNPACK_ROW_LENGTH); + GLint unpackAlignment = GetUnpackParam(GL_UNPACK_ALIGNMENT); + GLint unpackImageHeight = GetUnpackParam(GL_UNPACK_IMAGE_HEIGHT); + GLint unpackSkipImages = GetUnpackParam(GL_UNPACK_SKIP_IMAGES); + + GLsizei rowLength = (unpackRowLength > 0) ? unpackRowLength : width; + size_t bytesPerPixel = CalculateBytesPerPixel(format, type); + size_t componentSize = GetComponentSize(type); + + size_t srcRowSize = rowLength * bytesPerPixel; + size_t srcRowStride = (srcRowSize + unpackAlignment - 1) & ~(unpackAlignment - 1); + size_t srcImageStride = (unpackImageHeight > 0) ? + srcRowStride * unpackImageHeight : + srcRowStride * height; + + const GLubyte* srcData = static_cast(data); + srcData += unpackSkipImages * srcImageStride; + srcData += unpackSkipRows * srcRowStride; + srcData += unpackSkipPixels * bytesPerPixel; + + size_t dstRowStride = width * bytesPerPixel; + size_t dstSize = dstRowStride * height; + mip.pixelData.resize(dstSize); + GLubyte* dstData = mip.pixelData.data(); + + for (GLsizei y = 0; y < height; ++y) { + const GLubyte* srcRow = srcData + y * srcRowStride; + GLubyte* dstRow = dstData + y * dstRowStride; + + if (unpackSwapBytes) { + SwapBytesForTexture(format, type, srcRow, dstRow, width); + } + else if (unpackLSBFirst && componentSize == 1) { + ReverseBitOrder(srcRow, dstRow, width * bytesPerPixel); + } + else { + memcpy(dstRow, srcRow, width * bytesPerPixel); + } + } mip.hasData = true; - MG_Util::Debug::LogD("MG_State: Texture: Upload2D uploaded data size=%zu", dataSize); + MG_Util::Debug::LogD("MG_State: Texture: Upload2D uploaded %zu bytes with unpack params", dstSize); } else { mip.hasData = false; MG_Util::Debug::LogD("MG_State: Texture: Upload2D data pointer is null"); } - tex.data = data; tex.params.mipmapData[level] = mip; - MG_Util::Debug::LogD("MG_State: Texture: Upload2D succeeded for level=%d", level); - return GL_NO_ERROR; } @@ -285,114 +338,91 @@ GLenum TextureState::UpdateRegion2D(GLenum target, GLint level, GLint xoffset, MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D called target=0x%x, level=%d, x=%d, y=%d, w=%d, h=%d", target, level, xoffset, yoffset, width, height); - if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D invalid target=0x%x", target); - return GL_INVALID_ENUM; + GLenum validity = CheckUpdatingTextureRegion2DValidity(target, level, xoffset, yoffset, width, height, format, type, data); + if (validity != GL_NO_ERROR) { + MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D validation failed with error 0x%x", validity); + return validity; } - if (level < 0) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D invalid level=%d", level); - return GL_INVALID_VALUE; - } - - if (xoffset < 0 || yoffset < 0) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D negative offset x=%d, y=%d", xoffset, yoffset); - return GL_INVALID_VALUE; - } - - if (width < 0 || height < 0) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D negative dimensions w=%d, h=%d", width, height); - return GL_INVALID_VALUE; - } - - if (!data) { - // TODO: need to impl PBO and then impl here - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D data pointer is null"); - return GL_INVALID_OPERATION ; - } - GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); - if (boundTex == 0) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D no texture bound for target=0x%x", target); - return GL_INVALID_OPERATION; - } - - if (!IsTextureGenerated(boundTex)) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D texture %u not generated", boundTex); - return GL_INVALID_OPERATION; - } - - auto tex = textures[boundTex]; - - if (tex.IsImmutable()) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D texture is immutable"); - return GL_INVALID_OPERATION; - } - + auto& tex = textures[boundTex]; auto mipIt = tex.params.mipmapData.find(level); if (mipIt == tex.params.mipmapData.end()) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D no mipmap level %d", level); + MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D mipmap level %d not found", level); return GL_INVALID_OPERATION; } - TextureParams::MipmapLevel& mip = mipIt->second; - if (MG_Constants::Texture::VALID_FORMATS.find(format) == MG_Constants::Texture::VALID_FORMATS.end()) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D invalid format=0x%x", format); + GLint unpackSwapBytes = GetUnpackParam(GL_UNPACK_SWAP_BYTES); + GLint unpackLSBFirst = GetUnpackParam(GL_UNPACK_LSB_FIRST); + GLint unpackRowLength = GetUnpackParam(GL_UNPACK_ROW_LENGTH); + GLint unpackAlignment = GetUnpackParam(GL_UNPACK_ALIGNMENT); + GLint unpackSkipPixels = GetUnpackParam(GL_UNPACK_SKIP_PIXELS); + GLint unpackSkipRows = GetUnpackParam(GL_UNPACK_SKIP_ROWS); + GLint unpackImageHeight = GetUnpackParam(GL_UNPACK_IMAGE_HEIGHT); + GLint unpackSkipImages = GetUnpackParam(GL_UNPACK_SKIP_IMAGES); + + const size_t bytesPerPixel = CalculateBytesPerPixel(format, type); + const size_t srcSize = CalculatePixelDataSize(format, type, width, height); + + if (bytesPerPixel == 0) { + MG_Util::Debug::LogE("MG_State: Texture: Invalid format/type combination"); return GL_INVALID_ENUM; } - if (MG_Constants::Texture::VALID_TYPES.find(type) == MG_Constants::Texture::VALID_TYPES.end()) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D invalid type=0x%x", type); - return GL_INVALID_ENUM; - } - - bool typeNeedsRGB = (type == GL_UNSIGNED_BYTE_3_3_2 || type == GL_UNSIGNED_BYTE_2_3_3_REV || - type == GL_UNSIGNED_SHORT_5_6_5 || type == GL_UNSIGNED_SHORT_5_6_5_REV); - if (typeNeedsRGB && format != GL_RGB) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D type requires RGB format"); - return GL_INVALID_OPERATION; - } - - bool typeNeedsRGBA = (type == GL_UNSIGNED_SHORT_4_4_4_4 || type == GL_UNSIGNED_SHORT_4_4_4_4_REV || - type == GL_UNSIGNED_SHORT_5_5_5_1 || type == GL_UNSIGNED_SHORT_1_5_5_5_REV || - type == GL_UNSIGNED_INT_8_8_8_8 || type == GL_UNSIGNED_INT_8_8_8_8_REV || - type == GL_UNSIGNED_INT_10_10_10_2 || type == GL_UNSIGNED_INT_2_10_10_10_REV); - if (typeNeedsRGBA && format != GL_RGBA && format != GL_BGRA) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D type requires RGBA/BGRA format"); - return GL_INVALID_OPERATION; - } - if (xoffset + width > mip.width || yoffset + height > mip.height) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D region out of bounds: " - "x=%d+%d > %d or y=%d+%d > %d", - xoffset, width, mip.width, yoffset, height, mip.height); + MG_Util::Debug::LogE("MG_State: Texture: Update region out of bounds"); return GL_INVALID_VALUE; } - bool isDepthInternal = (mip.internalFormat == GL_DEPTH_COMPONENT || - mip.internalFormat == GL_DEPTH_COMPONENT16 || - mip.internalFormat == GL_DEPTH_COMPONENT24 || - mip.internalFormat == GL_DEPTH_COMPONENT32F || - mip.internalFormat == GL_DEPTH_STENCIL || - mip.internalFormat == GL_DEPTH24_STENCIL8 || - mip.internalFormat == GL_DEPTH32F_STENCIL8); + const GLsizei srcRowLength = (unpackRowLength > 0) ? unpackRowLength : width; + const size_t srcRowSize = srcRowLength * bytesPerPixel; + const size_t srcRowStride = (srcRowSize + unpackAlignment - 1) & ~(unpackAlignment - 1); + const size_t srcImageStride = (unpackImageHeight > 0) + ? srcRowStride * unpackImageHeight + : srcRowStride * height; - bool isDepthFormat = (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL); - if (isDepthInternal != isDepthFormat) { - MG_Util::Debug::LogE("MG_State: Texture: UpdateRegion2D format mismatch with internal format"); - return GL_INVALID_OPERATION; + const GLubyte* srcData = static_cast(data); + srcData += unpackSkipImages * srcImageStride; + srcData += unpackSkipRows * srcRowStride; + srcData += unpackSkipPixels * bytesPerPixel; + + const size_t requiredSrcSize = (height - 1) * srcRowStride + width * bytesPerPixel; + // TODO: Check the buffer data size. + + const size_t dstRowStride = mip.width * bytesPerPixel; + if (mip.pixelData.empty()) + mip.pixelData.resize(srcSize); + GLubyte* dstData = mip.pixelData.data() + yoffset * dstRowStride + xoffset * bytesPerPixel; + + MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D srcRowStride=%zu, dstRowStride=%zu", srcRowStride, dstRowStride); + MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D srcData=%p, dstData=%p", srcData, dstData); + MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D bytesPerPixel=%zu", bytesPerPixel); + + for (GLsizei y = 0; y < height; ++y) { + const GLubyte* srcRow = srcData + y * srcRowStride; + GLubyte* dstRow = dstData + y * dstRowStride; + + if (unpackSwapBytes) { + for (GLsizei x = 0; x < width; ++x) { + const GLubyte* srcPixel = srcRow + x * bytesPerPixel; + GLubyte* dstPixel = dstRow + x * bytesPerPixel; + SwapPixelBytes(format, type, srcPixel, dstPixel); + } + } else if (unpackLSBFirst && GetComponentSize(type) == 1) { + for (size_t i = 0; i < width * bytesPerPixel; ++i) { + dstRow[i] = ReverseBits(srcRow[i]); + } + } else { + memmove(dstRow, srcRow, width * bytesPerPixel); + } } mip.hasData = true; - MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D validated subimage update"); - - tex.data = data; - + MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D succeeded"); return GL_NO_ERROR; } - GLenum TextureState::SetTexturePropertyFloat(GLenum target, GLenum pname, GLfloat param) { MG_Util::Debug::LogD("MG_State: Texture: SetTexturePropertyFloat called target=0x%x, pname=0x%x, param=%f", target, pname, param); if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { @@ -818,6 +848,113 @@ GLenum TextureState::CheckUploadingTexture2DValidity(GLenum target, GLint level, return GL_NO_ERROR; } +GLenum TextureState::CheckUpdatingTextureRegion2DValidity(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLenum type, const GLvoid* data) { + MG_Util::Debug::LogD("MG_State: Texture: CheckUpdatingTextureRegion2DValidity called target=0x%x, level=%d, x=%d, y=%d, w=%d, h=%d", + target, level, xoffset, yoffset, width, height); + + if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (level < 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity invalid level=%d", level); + return GL_INVALID_VALUE; + } + + if (xoffset < 0 || yoffset < 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity negative offset x=%d, y=%d", xoffset, yoffset); + return GL_INVALID_VALUE; + } + + if (width < 0 || height < 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity negative dimensions w=%d, h=%d", width, height); + return GL_INVALID_VALUE; + } + + if (!data) { + // TODO: need to impl PBO and then impl here + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity data pointer is null"); + return GL_INVALID_OPERATION ; + } + + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); + if (boundTex == 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity no texture bound for target=0x%x", target); + return GL_INVALID_OPERATION; + } + + if (!IsTextureGenerated(boundTex)) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity texture %u not generated", boundTex); + return GL_INVALID_OPERATION; + } + + auto tex = textures[boundTex]; + + if (tex.IsImmutable()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity texture is immutable"); + return GL_INVALID_OPERATION; + } + + auto mipIt = tex.params.mipmapData.find(level); + if (mipIt == tex.params.mipmapData.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity no mipmap level %d", level); + return GL_INVALID_OPERATION; + } + + if (MG_Constants::Texture::VALID_FORMATS.find(format) == MG_Constants::Texture::VALID_FORMATS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity invalid format=0x%x", format); + return GL_INVALID_ENUM; + } + + if (MG_Constants::Texture::VALID_TYPES.find(type) == MG_Constants::Texture::VALID_TYPES.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity invalid type=0x%x", type); + return GL_INVALID_ENUM; + } + + bool typeNeedsRGB = (type == GL_UNSIGNED_BYTE_3_3_2 || type == GL_UNSIGNED_BYTE_2_3_3_REV || + type == GL_UNSIGNED_SHORT_5_6_5 || type == GL_UNSIGNED_SHORT_5_6_5_REV); + if (typeNeedsRGB && format != GL_RGB) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity type requires RGB format"); + return GL_INVALID_OPERATION; + } + + bool typeNeedsRGBA = (type == GL_UNSIGNED_SHORT_4_4_4_4 || type == GL_UNSIGNED_SHORT_4_4_4_4_REV || + type == GL_UNSIGNED_SHORT_5_5_5_1 || type == GL_UNSIGNED_SHORT_1_5_5_5_REV || + type == GL_UNSIGNED_INT_8_8_8_8 || type == GL_UNSIGNED_INT_8_8_8_8_REV || + type == GL_UNSIGNED_INT_10_10_10_2 || type == GL_UNSIGNED_INT_2_10_10_10_REV); + if (typeNeedsRGBA && format != GL_RGBA && format != GL_BGRA) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity type requires RGBA/BGRA format"); + return GL_INVALID_OPERATION; + } + + TextureParams::MipmapLevel& mip = mipIt->second; + if (xoffset + width > mip.width || yoffset + height > mip.height) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity region out of bounds: " + "x=%d+%d > %d or y=%d+%d > %d", + xoffset, width, mip.width, yoffset, height, mip.height); + return GL_INVALID_VALUE; + } + + bool isDepthInternal = (mip.internalFormat == GL_DEPTH_COMPONENT || + mip.internalFormat == GL_DEPTH_COMPONENT16 || + mip.internalFormat == GL_DEPTH_COMPONENT24 || + mip.internalFormat == GL_DEPTH_COMPONENT32F || + mip.internalFormat == GL_DEPTH_STENCIL || + mip.internalFormat == GL_DEPTH24_STENCIL8 || + mip.internalFormat == GL_DEPTH32F_STENCIL8); + + bool isDepthFormat = (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL); + if (isDepthInternal != isDepthFormat) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUpdatingTextureRegion2DValidity format mismatch with internal format"); + return GL_INVALID_OPERATION; + } + MG_Util::Debug::LogD("MG_State: Texture: CheckUpdatingTextureRegion2DValidity validated subimage update"); + return GL_NO_ERROR; +} + ComponentSizes TextureState::GetComponentSizes(GLenum internalFormat) { MG_Util::Debug::LogD("MG_State: Texture: GetComponentSizes called for internalFormat=0x%x", internalFormat); ComponentSizes sizes = {0}; @@ -935,3 +1072,118 @@ ComponentSizes TextureState::GetComponentSizes(GLenum internalFormat) { sizes.red, sizes.green, sizes.blue, sizes.alpha, sizes.depth, sizes.stencil, sizes.isCompressed); return sizes; } + +size_t TextureState::CalculateBytesPerPixel(GLenum format, GLenum type) { + switch (type) { + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + return 1; + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return 2; + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + return 4; + default: + break; + } + + int components = 0; + switch (format) { + case GL_RED: + case GL_DEPTH_COMPONENT: + components = 1; + break; + case GL_RG: + case GL_DEPTH_STENCIL: + components = 2; + break; + case GL_RGB: + case GL_BGR: + components = 3; + break; + case GL_RGBA: + case GL_BGRA: + components = 4; + break; + default: + components = 0; + break; + } + + size_t componentSize = GetComponentSize(type); + return components * componentSize; +} + +size_t TextureState::GetComponentSize(GLenum type) { + switch (type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + return 4; + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + return 1; + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return 2; + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + return 4; + default: + return 0; + } +} + +void TextureState::SwapBytesForTexture(GLenum format, GLenum type, const GLubyte* src, GLubyte* dst, GLsizei width) { + size_t componentSize = GetComponentSize(type); + if (componentSize <= 1) { + memcpy(dst, src, width * componentSize); + return; + } + + for (GLsizei i = 0; i < width; ++i) { + for (size_t j = 0; j < componentSize; ++j) { + dst[i * componentSize + j] = src[i * componentSize + (componentSize - 1 - j)]; + } + } +} + +GLubyte TextureState::ReverseBits(GLubyte b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void TextureState::ReverseBitOrder(const GLubyte* src, GLubyte* dst, size_t byteCount) { + for (size_t i = 0; i < byteCount; ++i) { + dst[i] = ReverseBits(src[i]); + } +} + +void TextureState::SwapPixelBytes(GLenum format, GLenum type, const GLubyte* src, GLubyte* dst) { + const size_t size = GetComponentSize(type); + for (size_t i = 0; i < size; ++i) { + dst[i] = src[size - 1 - i]; + } +} diff --git a/MG/MG_GL/State/Texture/TextureState.h b/MG/MG_GL/State/Texture/TextureState.h index 3837f7ae..75411545 100644 --- a/MG/MG_GL/State/Texture/TextureState.h +++ b/MG/MG_GL/State/Texture/TextureState.h @@ -40,7 +40,7 @@ public: TextureParams params; const void* data = nullptr; bool IsImmutable() const; - uint64_t createTimestamp; + uint64_t createTimestamp{}; }; class TextureUnitState { @@ -62,10 +62,11 @@ private: std::set freeIDs; std::unordered_map proxyTextures; + + static GLint GetUnpackParam(GLenum pname); public: TextureState(); - static TextureState& GetInstance(); bool IsTextureGenerated(GLuint texture); bool IsTexture(GLuint texture); std::unordered_map textures; @@ -88,12 +89,22 @@ public: GLenum GetLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params); private: - static static size_t CalculatePixelDataSize(GLenum format, GLenum type, GLsizei width, GLsizei height); + size_t CalculatePixelDataSize(GLenum format, GLenum type, GLsizei width, GLsizei height); void InvalidateTextureInAllUnits(GLuint texture); static ComponentSizes GetComponentSizes(GLenum internalFormat); + size_t CalculateBytesPerPixel(GLenum format, GLenum type); + static size_t GetComponentSize(GLenum type); + void SwapBytesForTexture(GLenum format, GLenum type, const GLubyte* src, GLubyte* dst, GLsizei width); + static void ReverseBitOrder(const GLubyte* src, GLubyte* dst, size_t size); + static GLubyte ReverseBits(GLubyte b); + void SwapPixelBytes(GLenum format, GLenum type, const GLubyte* src, GLubyte* dst); + GLenum CheckUploadingTexture2DValidity(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data); + GLenum CheckUpdatingTextureRegion2DValidity(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLenum type, const GLvoid* data); }; #endif //MOBILEGL_TEXTURESTATE_H