From 41fefb8e5481187e3ca48af5a34f8604efc3e8ad Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Fri, 4 Apr 2025 23:01:09 +0800 Subject: [PATCH] [Feat] (...): Add global OpenGL state. Add gl texture state (almost complete for Minecraft) and implementation gl texture functions that Minecraft would use. Add gl error state. --- CMakeLists.txt | 5 + MG/Constants.h | 56 ++ MG/Includes.h | 7 + MG/Init.cpp | 6 + MG/MG_GL/GL/Getter/GL_Getter.cpp | 9 +- MG/MG_GL/GL/Getter/GL_Getter.h | 1 - MG/MG_GL/GL/Texture/GL_Texture.cpp | 86 ++- MG/MG_GL/GL/Texture/GL_Texture.h | 10 +- MG/MG_GL/GL_Def/GL_Def.cpp | 22 +- MG/MG_GL/State/Core/GLState.cpp | 86 +++ MG/MG_GL/State/Core/GLState.h | 42 ++ MG/MG_GL/State/Texture/TextureState.cpp | 935 ++++++++++++++++++++++++ MG/MG_GL/State/Texture/TextureState.h | 98 +++ 13 files changed, 1343 insertions(+), 20 deletions(-) create mode 100644 MG/MG_GL/State/Core/GLState.cpp create mode 100644 MG/MG_GL/State/Core/GLState.h create mode 100644 MG/MG_GL/State/Texture/TextureState.cpp create mode 100644 MG/MG_GL/State/Texture/TextureState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b5aaa006..ba7dab26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,11 @@ add_library(${CMAKE_PROJECT_NAME} SHARED MG/MG_GL/GL/Texture/GL_Texture.cpp MG/MG_GL/GL/VertexArray/GL_VertexArray.cpp + + # MG_GL/State + MG/MG_GL/State/Core/GLState.cpp + + MG/MG_GL/State/Texture/TextureState.cpp # MG_GL/GLX MG/MG_GL/GLX/LookUp/LookUp.cpp diff --git a/MG/Constants.h b/MG/Constants.h index d98dd2fa..2474424d 100644 --- a/MG/Constants.h +++ b/MG/Constants.h @@ -10,6 +10,10 @@ #endif #define MG_EXPORT __attribute__((visibility("default"))) +#include +#include +#include + namespace MG_Constants { namespace Common { inline const int LOG_LEVEL_DEBUG = 0x0010; @@ -18,6 +22,58 @@ namespace MG_Constants { inline const int LOG_LEVEL_INFO = 0x0013; inline const int LOG_LEVEL_FATAL = 0x0014; } + + namespace Texture { + inline const GLuint MAX_TEXTURE_UNITS = 80; + const GLsizei MAX_TEXTURE_SIZE = 32768; + const GLsizei MAX_ARRAY_LAYERS = 256; + inline const std::unordered_set VALID_TARGETS = { + GL_TEXTURE_2D, GL_PROXY_TEXTURE_2D, GL_TEXTURE_1D_ARRAY, GL_PROXY_TEXTURE_1D_ARRAY, + GL_TEXTURE_RECTANGLE, GL_PROXY_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_PROXY_TEXTURE_CUBE_MAP + }; // OpenGL 3 + + inline const std::unordered_set VALID_FORMATS = { + GL_RED, GL_RG, GL_RGB, GL_BGR, GL_RGBA, GL_BGRA, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL + }; // OpenGL 3 + + inline const std::unordered_set VALID_INTERNAL_FORMATS = { + GL_RED, GL_RG, GL_RGB, GL_BGR, GL_RGBA, GL_BGRA, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL, + GL_RGBA32F, GL_RGBA32I, GL_RGBA32UI, GL_RGBA16, GL_RGBA16F, GL_RGBA16I, + GL_RGBA16UI, GL_RGBA8, GL_RGBA8UI, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGB10_A2UI, + GL_R11F_G11F_B10F, GL_RG32F, GL_RG32I, GL_RG32UI, GL_RG16, GL_RG16F, GL_RGB16I, + GL_RGB16UI, GL_RG8, GL_RG8I, GL_RG8UI, GL_R32F, GL_R32I, GL_R32UI, GL_R16F, + GL_R16I, GL_R16UI, GL_R8, GL_R8I, GL_R8UI, GL_RGBA16_SNORM, GL_RGBA8_SNORM, + GL_RGB32F, GL_RGB32I, GL_RGB32UI, GL_RGB16_SNORM, GL_RGB16F, GL_RGB16I, GL_RGB16UI, + GL_RGB16, GL_RGB8_SNORM, GL_RGB8, GL_RGB8I, GL_RGB8UI, GL_SRGB8, GL_RGB9_E5, + GL_RG16_SNORM, GL_RG8_SNORM, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_SIGNED_RG_RGTC2, + GL_R16_SNORM, GL_R8_SNORM, GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_SIGNED_RED_RGTC1, + GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT16, GL_DEPTH32F_STENCIL8, + GL_DEPTH24_STENCIL8 + }; // OpenGL 3 + + inline const std::unordered_set VALID_TYPES = { + GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, + GL_FLOAT, GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, GL_UNSIGNED_SHORT_5_6_5, + GL_UNSIGNED_SHORT_5_6_5_REV, GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, + GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, GL_UNSIGNED_INT_8_8_8_8, + GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, GL_UNSIGNED_INT_2_10_10_10_REV + }; // OpenGL 3 + + inline const std::unordered_set VALID_TEXTURE_PARAM_NAMES = { + GL_TEXTURE_BASE_LEVEL, GL_TEXTURE_COMPARE_FUNC, GL_TEXTURE_COMPARE_MODE, GL_TEXTURE_MIN_FILTER, + GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MAX_LEVEL, GL_TEXTURE_SWIZZLE_R, GL_TEXTURE_SWIZZLE_G, + GL_TEXTURE_SWIZZLE_B, GL_TEXTURE_SWIZZLE_A, GL_TEXTURE_SWIZZLE_RGBA, GL_TEXTURE_WRAP_S, + GL_TEXTURE_WRAP_T, GL_TEXTURE_WRAP_R, GL_TEXTURE_LOD_BIAS, GL_TEXTURE_MIN_LOD, GL_TEXTURE_MAX_LOD, GL_TEXTURE_BORDER_COLOR + }; // OpenGL 3 + + inline const std::unordered_set VALID_QUERY_LEVEL_PROPERTY_PARAM_NAMES = { + GL_TEXTURE_WIDTH, GL_TEXTURE_HEIGHT, GL_TEXTURE_DEPTH, GL_TEXTURE_INTERNAL_FORMAT, + GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE, + GL_TEXTURE_DEPTH_SIZE, GL_TEXTURE_COMPRESSED, GL_TEXTURE_COMPRESSED_IMAGE_SIZE + }; // OpenGL 3 + } namespace Backend { #define BACKEND_VULKAN 0x0015 diff --git a/MG/Includes.h b/MG/Includes.h index 19e538ad..bbf5e9a0 100644 --- a/MG/Includes.h +++ b/MG/Includes.h @@ -32,6 +32,12 @@ #ifndef MOBILEGL_GL_VERTEXARRAY_H #include "MG_GL/GL/VertexArray/GL_VertexArray.h" #endif +#ifndef MOBILEGL_GLSTATE_H +#include "MG_GL/State/Core/GLState.h" +#endif +#ifndef MOBILEGL_TEXTURESTATE_H +#include "MG_GL/State/Texture/TextureState.h" +#endif #ifndef MOBILEGL_LOOKUP_H #include "MG_GL/GLX/LookUp/LookUp.h" #endif @@ -64,6 +70,7 @@ #include #include #include +#include #include #include #include diff --git a/MG/Init.cpp b/MG/Init.cpp index 9b8847b0..9550c484 100644 --- a/MG/Init.cpp +++ b/MG/Init.cpp @@ -7,4 +7,10 @@ __attribute__((constructor)) void MG_Initialize() { MG_Util::Debug::LogInit(); MG_Util::Debug::LogI("MobileGL Initializing..."); + MG_State::Init(); +} + +__attribute__((destructor)) void MG_Destroy() { + MG_Util::Debug::LogI("MobileGL Exiting..."); + MG_State::Destroy(); } \ No newline at end of file diff --git a/MG/MG_GL/GL/Getter/GL_Getter.cpp b/MG/MG_GL/GL/Getter/GL_Getter.cpp index 069c16bf..bb4c2614 100644 --- a/MG/MG_GL/GL/Getter/GL_Getter.cpp +++ b/MG/MG_GL/GL/Getter/GL_Getter.cpp @@ -110,8 +110,9 @@ namespace MG_GL::GL { } GLenum GetError() { - MG_Util::Debug::LogD("glGetError"); - return GL_NO_ERROR; + GLenum error = MG_State::GetError(); + MG_Util::Debug::LogD("glGetError -> %s", MG_Util::Debug::GLEnumToString(error)); + return error; } void GetIntegerv(GLenum pname, GLint *params) { @@ -153,8 +154,4 @@ namespace MG_GL::GL { break; } } - - void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params) { - // TODO - } } \ No newline at end of file diff --git a/MG/MG_GL/GL/Getter/GL_Getter.h b/MG/MG_GL/GL/Getter/GL_Getter.h index ba6e1322..24b409a1 100644 --- a/MG/MG_GL/GL/Getter/GL_Getter.h +++ b/MG/MG_GL/GL/Getter/GL_Getter.h @@ -12,7 +12,6 @@ namespace MG_GL::GL { const ::GLubyte* GetStringi(GLenum name, GLuint index); ::GLenum GetError(); void GetIntegerv(GLenum pname, GLint *params); - void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params); } #endif //MOBILEGL_GL_GETTER_H diff --git a/MG/MG_GL/GL/Texture/GL_Texture.cpp b/MG/MG_GL/GL/Texture/GL_Texture.cpp index 4c08d6c7..ac67782a 100644 --- a/MG/MG_GL/GL/Texture/GL_Texture.cpp +++ b/MG/MG_GL/GL/Texture/GL_Texture.cpp @@ -5,5 +5,89 @@ #include "GL_Texture.h" namespace MG_GL::GL { - + void ActiveTexture(GLenum texture) { + MG_Util::Debug::LogD("glActiveTexture, texture: %d", texture); + GLenum result = MG_State::BindTextureUnit(texture); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void BindTexture(GLenum target, GLuint texture) { + MG_Util::Debug::LogD("glBindTexture, target: %d, texture: %d", target, texture); + GLenum result = MG_State::BindTexture(target, texture); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void DeleteTextures(GLsizei n, const GLuint* textures) { + MG_Util::Debug::LogD("glDeleteTextures, n: %d, textures: %p", n, textures); + GLenum result = MG_State::DeleteTextures(n, textures); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void GenTextures(GLsizei n, GLuint* textures) { + MG_Util::Debug::LogD("glGenTextures, n: %d, textures: %p", n, textures); + GLenum result = MG_State::CreateTextures(n, textures); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void TexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const void* data) { + MG_Util::Debug::LogD("glTexImage2D, target: %d, level: %d, internalFormat: %d, width: %d, height: %d, format: %d, type: %d, data: %p", + target, level, internalFormat, width, height, format, type, data); + GLenum result = MG_State::UploadTexture2D(target, level, internalFormat, width, height, border, format, type, data); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void TexParameterf(GLenum target, GLenum pname, GLfloat param) { + MG_Util::Debug::LogD("glTexParameterf, target: %d, pname: %d, param: %f", target, pname, param); + GLenum result = MG_State::SetTexturePropertyFloat(target, 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)); + } + + void TexParameteri(GLenum target, GLenum pname, GLint param) { + MG_Util::Debug::LogD("glTexParameteri, target: %d, pname: %d, param: %d", target, pname, param); + GLenum result = MG_State::SetTexturePropertyInt(target, 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)); + } + + void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + GLsizei height, GLenum format, GLenum type, const void* pixels) { + MG_Util::Debug::LogD("glTexSubImage2D, target: %d, level: %d, xoffset: %d, yoffset: %d, width: %d, height: %d, format: %d, type: %d, pixels: %p", + target, level, xoffset, yoffset, width, height, format, type, pixels); + GLenum result = MG_State::UpdateTextureRegion2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + if (result == GL_NO_ERROR) + return; + MG_State::SetError(result); + MG_Util::Debug::LogE("Error from MG State: %s", MG_Util::Debug::GLEnumToString(result)); + } + + void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params) { + MG_Util::Debug::LogD("glGetTexLevelParameteriv, target: %d, level: %d, pname: %d, params: %p", + target, level, pname, params); + GLenum result = MG_State::GetTextureLevelPropertyIntVector(target, level, pname, params); + 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/Texture/GL_Texture.h b/MG/MG_GL/GL/Texture/GL_Texture.h index 3108c2a8..758d0467 100644 --- a/MG/MG_GL/GL/Texture/GL_Texture.h +++ b/MG/MG_GL/GL/Texture/GL_Texture.h @@ -8,7 +8,15 @@ #include "../../../Includes.h" namespace MG_GL::GL { - + void ActiveTexture(GLenum texture); + void BindTexture(GLenum target, GLuint texture); + void DeleteTextures(GLsizei n, const GLuint* textures); + void GenTextures(GLsizei n, GLuint* textures); + void TexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* data); + void TexParameterf(GLenum target, GLenum pname, GLfloat param); + void TexParameteri(GLenum target, GLenum pname, GLint param); + void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); + void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params); } #endif //MOBILEGL_GL_TEXTURE_H diff --git a/MG/MG_GL/GL_Def/GL_Def.cpp b/MG/MG_GL/GL_Def/GL_Def.cpp index 2caf8898..80166cc8 100644 --- a/MG/MG_GL/GL_Def/GL_Def.cpp +++ b/MG/MG_GL/GL_Def/GL_Def.cpp @@ -20,12 +20,12 @@ extern "C" MG_EXPORT type gl##name(__VA_ARGS__) { extern "C" MG_EXPORT type gl##name(__VA_ARGS__) { #define DECLARE_GL_FUNCTION_END(type,name,...) \ - MG_Util::Debug::LogD("Emulating function: %s(...)", __FUNCTION__); \ + MG_Util::Debug::LogD("Implementing function: %s(...)", __FUNCTION__); \ return MG_GL::GL::name(__VA_ARGS__); \ } #define DECLARE_GL_FUNCTION_END_NO_RETURN(type,name,...) \ - MG_Util::Debug::LogD("Emulating function: %s(...)", __FUNCTION__); \ + MG_Util::Debug::LogD("Implementing function: %s(...)", __FUNCTION__); \ MG_GL::GL::name(__VA_ARGS__); \ } @@ -33,13 +33,13 @@ DECLARE_GL_FUNCTION_HEAD(const GLubyte*, GetStringi, GLenum name, GLuint index) DECLARE_GL_FUNCTION_HEAD(const GLubyte*, GetString, GLenum name) DECLARE_GL_FUNCTION_END(const GLubyte*, GetString, name) DECLARE_GL_FUNCTION_STUB_HEAD(void*, MapBuffer, GLenum target, GLenum access) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void*, MapBuffer, target, access) DECLARE_GL_FUNCTION_HEAD(void, ClearDepth, GLclampd depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearDepth, depth) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ActiveTexture, GLenum texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ActiveTexture, texture) +DECLARE_GL_FUNCTION_HEAD(void, ActiveTexture, GLenum texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ActiveTexture, texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, AttachShader, GLuint program, GLuint shader) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, AttachShader, program,shader) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindAttribLocation, GLuint program, GLuint index, const GLchar *name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindAttribLocation, program,index,name) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindBuffer, GLenum target, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindBuffer, target,buffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFramebuffer, GLenum target, GLuint framebuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFramebuffer, target,framebuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindRenderbuffer, target,renderbuffer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTexture, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTexture, target,texture) +DECLARE_GL_FUNCTION_HEAD(void, BindTexture, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTexture, target,texture) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendColor, red,green,blue,alpha) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquation, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquation, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparate, modeRGB,modeAlpha) @@ -66,7 +66,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteFramebuffers, GLsizei n, const GLuint DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteProgram, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteProgram, program) DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteRenderbuffers, n,renderbuffers) DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteShader, GLuint shader) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteShader, shader) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteTextures, GLsizei n, const GLuint *textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteTextures, n,textures) +DECLARE_GL_FUNCTION_HEAD(void, DeleteTextures, GLsizei n, const GLuint *textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteTextures, n,textures) DECLARE_GL_FUNCTION_STUB_HEAD(void, DepthFunc, GLenum func) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DepthFunc, func) DECLARE_GL_FUNCTION_STUB_HEAD(void, DepthMask, GLboolean flag) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DepthMask, flag) DECLARE_GL_FUNCTION_STUB_HEAD(void, DepthRangef, GLfloat n, GLfloat f) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DepthRangef, n,f) @@ -86,7 +86,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GenBuffers, GLsizei n, GLuint *buffers) DECL DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateMipmap, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateMipmap, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenFramebuffers, GLsizei n, GLuint *framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenFramebuffers, n,framebuffers) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenRenderbuffers, GLsizei n, GLuint *renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenRenderbuffers, n,renderbuffers) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GenTextures, GLsizei n, GLuint *textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenTextures, n,textures) +DECLARE_GL_FUNCTION_HEAD(void, GenTextures, GLsizei n, GLuint *textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenTextures, n,textures) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveAttrib, program,index,bufSize,length,size,type,name) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniform, program,index,bufSize,length,size,type,name) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetAttachedShaders, program,maxCount,count,shaders) @@ -137,12 +137,12 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, StencilMask, GLuint mask) DECLARE_GL_FUNCTIO DECLARE_GL_FUNCTION_STUB_HEAD(void, StencilMaskSeparate, GLenum face, GLuint mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, StencilMaskSeparate, face,mask) DECLARE_GL_FUNCTION_STUB_HEAD(void, StencilOp, GLenum fail, GLenum zfail, GLenum zpass) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, StencilOp, fail,zfail,zpass) DECLARE_GL_FUNCTION_STUB_HEAD(void, StencilOpSeparate, GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, StencilOpSeparate, face,sfail,dpfail,dppass) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexImage2D, target,level,internalformat,width,height,border,format,type,pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexParameterf, target,pname,param) +DECLARE_GL_FUNCTION_HEAD(void, TexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2D, target,level,internalformat,width,height,border,format,type,pixels) +DECLARE_GL_FUNCTION_HEAD(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameterf, target,pname,param) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexParameterfv, GLenum target, GLenum pname, const GLfloat *params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexParameterfv, target,pname,params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexParameteri, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexParameteri, target,pname,param) +DECLARE_GL_FUNCTION_HEAD(void, TexParameteri, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexParameteri, target,pname,param) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexParameteriv, GLenum target, GLenum pname, const GLint *params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexParameteriv, target,pname,params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexSubImage2D, target,level,xoffset,yoffset,width,height,format,type,pixels) +DECLARE_GL_FUNCTION_HEAD(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexSubImage2D, target,level,xoffset,yoffset,width,height,format,type,pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1f, GLint location, GLfloat v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1f, location,v0) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1fv, GLint location, GLsizei count, const GLfloat *value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1fv, location,count,value) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1i, GLint location, GLint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1i, location,v0) @@ -337,7 +337,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, MemoryBarrierByRegion, GLbitfield barriers) DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2DMultisample, target,samples,internalformat,width,height,fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMultisamplefv, GLenum pname, GLuint index, GLfloat *val) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMultisamplefv, pname,index,val) DECLARE_GL_FUNCTION_STUB_HEAD(void, SampleMaski, GLuint maskNumber, GLbitfield mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SampleMaski, maskNumber,mask) -DECLARE_GL_FUNCTION_HEAD(void, GetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTexLevelParameteriv, target,level,pname,params) +DECLARE_GL_FUNCTION_HEAD(void, GetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexLevelParameteriv, target,level,pname,params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat *params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTexLevelParameterfv, target,level,pname,params) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindVertexBuffer, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindVertexBuffer, bindingindex,buffer,offset,stride) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribFormat, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribFormat, attribindex,size,type,normalized,relativeoffset) diff --git a/MG/MG_GL/State/Core/GLState.cpp b/MG/MG_GL/State/Core/GLState.cpp new file mode 100644 index 00000000..0cae0f41 --- /dev/null +++ b/MG/MG_GL/State/Core/GLState.cpp @@ -0,0 +1,86 @@ +// +// Created by BZLZHH on 2025/4/4. +// + +#include "GLState.h" + +TextureState* MG_State_T::textureState; +std::queue MG_State_T::glErrorQueue; + +namespace MG_State { + MG_State_T* MG_State_O; + + void Init() { + MG_State_O = new MG_State_T(); + MG_State_T::textureState = new TextureState(); + } + + void Destroy() { + delete MG_State_O; + } + + void SetError(GLenum error) { + if (error == GL_NO_ERROR) { + return; + } + MG_State_T::glErrorQueue.push(error); + } + + GLenum GetError() { + if (MG_State_T::glErrorQueue.empty()) { + return GL_NO_ERROR; + } + GLenum error = MG_State_T::glErrorQueue.front(); + MG_State_T::glErrorQueue.pop(); + return error; + } + + // Texture + GLenum BindTextureUnit(GLenum textureUnit) { + return MG_State_T::textureState->BindUnit(textureUnit); + } + + GLenum CreateTexture(GLuint* texture) { + return MG_State_T::textureState->Create(texture); + } + + GLenum CreateTextures(GLsizei n, GLuint* textures) { + return MG_State_T::textureState->CreateN(n, textures); + } + + GLenum BindTexture(GLenum target, GLuint texture) { + return MG_State_T::textureState->Bind(target, texture); + } + + GLenum UploadTexture2D(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const void* data) { + return MG_State_T::textureState->Upload2D(target, level, internalFormat, width, height, border, format, type, data); + } + + GLenum UpdateTextureRegion2D(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLenum type, const GLvoid* data) { + return MG_State_T::textureState->UpdateRegion2D(target, level, xoffset, yoffset, width, height, format, type, data); + } + + GLenum SetTexturePropertyInt(GLenum target, GLenum pname, GLint param) { + return MG_State_T::textureState->SetTexturePropertyInt(target, pname, param); + } + + GLenum SetTexturePropertyFloat(GLenum target, GLenum pname, GLfloat param) { + return MG_State_T::textureState->SetTexturePropertyFloat(target, pname, param); + } + + GLenum DeleteTexture(GLuint texture) { + return MG_State_T::textureState->Delete(texture); + } + + GLenum DeleteTextures(GLsizei n, const GLuint* textures) { + return MG_State_T::textureState->DeleteN(n, textures); + } + + GLenum GetTextureLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params) { + return MG_State_T::textureState->GetLevelPropertyIntVector(target, level, pname, params); + } +} \ No newline at end of file diff --git a/MG/MG_GL/State/Core/GLState.h b/MG/MG_GL/State/Core/GLState.h new file mode 100644 index 00000000..e68fd07b --- /dev/null +++ b/MG/MG_GL/State/Core/GLState.h @@ -0,0 +1,42 @@ +// +// Created by BZLZHH on 2025/4/4. +// + +#ifndef MOBILEGL_GLSTATE_H +#define MOBILEGL_GLSTATE_H + +#include "../../../Includes.h" + +struct MG_State_T { + static std::queue glErrorQueue; + static TextureState* textureState; +}; + +namespace MG_State { + extern MG_State_T* MG_State_O; + + void Init(); + void Destroy(); + void SetError(GLenum error); + GLenum GetError(); + + //Texture + GLenum BindTextureUnit(GLenum textureUnit); + GLenum CreateTexture(GLuint* texture); + GLenum CreateTextures(GLsizei n, GLuint* textures); + GLenum BindTexture(GLenum target, GLuint texture); + GLenum UploadTexture2D(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const void* data); + GLenum UpdateTextureRegion2D(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLenum type, const GLvoid* data); + GLenum SetTexturePropertyInt(GLenum target, GLenum pname, GLint param); + GLenum SetTexturePropertyFloat(GLenum target, GLenum pname, GLfloat param); + GLenum DeleteTexture(GLuint texture); + GLenum DeleteTextures(GLsizei n, const GLuint* textures); + GLenum GetTextureLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params); +} + + +#endif //MOBILEGL_GLSTATE_H diff --git a/MG/MG_GL/State/Texture/TextureState.cpp b/MG/MG_GL/State/Texture/TextureState.cpp new file mode 100644 index 00000000..aca5c1dd --- /dev/null +++ b/MG/MG_GL/State/Texture/TextureState.cpp @@ -0,0 +1,935 @@ +// +// Created by BZLZHH on 2025/4/4. +// + +#include "TextureState.h" + +// TextureObject + +bool TextureObject::IsImmutable() const { + bool immutable = params.texPropertiesInt.count(GL_TEXTURE_IMMUTABLE_FORMAT); + MG_Util::Debug::LogD("MG_State: Texture: TextureObject::IsImmutable returns %d", immutable); + return immutable; +} + +// TextureUnitState + +void TextureUnitState::Bind(GLenum target, GLuint texture) { + MG_Util::Debug::LogD("MG_State: Texture: TextureUnitState::Bind target=0x%x texture=%u", target, texture); + boundTextures[target] = texture; +} + +GLuint TextureUnitState::GetBoundTexture(GLenum target) { + auto it = boundTextures.find(target); + GLuint tex = (it != boundTextures.end()) ? it->second : 0; + MG_Util::Debug::LogD("MG_State: Texture: TextureUnitState::GetBoundTexture target=0x%x returns %u", target, tex); + return tex; +} + +// TextureState + +TextureState::TextureState() { + textureUnits.resize(MG_Constants::Texture::MAX_TEXTURE_UNITS); + 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; +} + +bool TextureState::IsTextureGenerated(GLuint texture) { + bool isValidTextureNumber = texture != 0; + bool isValidTexture; + bool generated = false; + if (!isValidTextureNumber) { + MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated texture == 0"); + } else { + isValidTexture = textures.find(texture) != textures.end(); + if (!isValidTexture) { + MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated invalid texture %d", texture); + } else { + generated = textures.at(texture).generated; + if (!generated) { + MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated texture %d not generated", texture, generated); + } + } + } + MG_Util::Debug::LogD("MG_State: Texture: IsTextureGenerated texture=%u returns %d", texture, generated); + return generated; +} + +bool TextureState::IsTexture(GLuint texture) { + bool isTex = texture != 0 && textures.find(texture) != textures.end(); + MG_Util::Debug::LogD("MG_State: Texture: IsTexture texture=%u returns %d", texture, isTex); + return isTex; +} + +GLenum TextureState::BindUnit(GLenum textureUnit) { + MG_Util::Debug::LogD("MG_State: Texture: BindUnit textureUnit=0x%x", textureUnit); + GLuint unitIndex = textureUnit - GL_TEXTURE0; + if (unitIndex < textureUnits.size()) { + activeTextureUnit = unitIndex; + MG_Util::Debug::LogD("MG_State: Texture: BindUnit set activeTextureUnit=%u", activeTextureUnit); + } else { + MG_Util::Debug::LogE("MG_State: Texture: BindUnit invalid texture unit 0x%x", textureUnit); + return GL_INVALID_ENUM; + } + return GL_NO_ERROR; +} + +GLenum TextureState::Create(GLuint* texture) { + MG_Util::Debug::LogD("MG_State: Texture: Create called"); + if (!texture) { + MG_Util::Debug::LogE("MG_State: Texture: Create received null pointer"); + return GL_INVALID_VALUE; + } + return CreateN(1, texture); +} + +GLenum TextureState::CreateN(GLsizei n, GLuint* textures) { + MG_Util::Debug::LogD("MG_State: Texture: CreateN called with n=%d", n); + if (n < 1) { + MG_Util::Debug::LogE("MG_State: Texture: CreateN invalid n=%d", n); + return GL_INVALID_VALUE; + } + + for (GLsizei i = 0; i < n; ++i) { + GLuint id = 0; + if (!freeIDs.empty()) { + id = *freeIDs.begin(); + freeIDs.erase(freeIDs.begin()); + MG_Util::Debug::LogD("MG_State: Texture: CreateN reusing free id=%u", id); + } else { + id = ++lastUsedID; + MG_Util::Debug::LogD("MG_State: Texture: CreateN new id=%u", id); + } + TextureObject obj; + obj.generated = true; + this->textures[id] = obj; + textures[i] = id; + } + return GL_NO_ERROR; +} + +GLenum TextureState::Bind(GLenum target, GLuint texture) { + MG_Util::Debug::LogD("MG_State: Texture: Bind called with target=0x%x, texture=%u", target, texture); + if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: Bind invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (texture == 0) { + TextureUnitState& unit = textureUnits[activeTextureUnit]; + unit.Bind(target, 0); + MG_Util::Debug::LogD("MG_State: Texture: Bind unbind succeeded for target=0x%x", target); + return GL_NO_ERROR; + } + + if (!IsTextureGenerated(texture)) { + MG_Util::Debug::LogE("MG_State: Texture: Bind texture %u not generated", texture); + return GL_INVALID_VALUE; + } + + auto texObjIt = textures.find(texture); + if(texObjIt != textures.end()){ + if (texObjIt->second.target != target && texObjIt->second.target != GL_NONE) { + MG_Util::Debug::LogE("MG_State: Texture: Bind texture %u bound to different target 0x%x", texture, texObjIt->second.target); + return GL_INVALID_OPERATION; + } + } + + + TextureUnitState& unit = textureUnits[activeTextureUnit]; + TextureObject* texObj = nullptr; + auto it = this->textures.find(texture); + if (it == this->textures.end()) { + MG_Util::Debug::LogW("MG_State: Texture: Bind texture %u not found, creating new (non-generated)", texture); + TextureObject newObj; + newObj.generated = false; + this->textures[texture] = newObj; + texObj = &this->textures[texture]; + } else { + texObj = &it->second; + } + + if (texObj->target == GL_NONE) { + texObj->target = target; + MG_Util::Debug::LogD("MG_State: Texture: Bind setting target=0x%x for texture %u", target, texture); + } else if (texObj->target != target) { + MG_Util::Debug::LogE("MG_State: Texture: Bind mismatch target for texture %u, expected 0x%x, got 0x%x", texture, texObj->target, target); + return GL_INVALID_OPERATION; + } + + unit.Bind(target, texture); + MG_Util::Debug::LogD("MG_State: Texture: Bind succeeded for texture %u on target=0x%x", texture, target); + + return GL_NO_ERROR; +} + +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); + size_t bytesPerPixel = 0; + switch (type) { + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + bytesPerPixel = 1; + break; + 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: + bytesPerPixel = 2; + break; + 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: + bytesPerPixel = 4; + default: { + int components = 0; + switch (format) { + case GL_RED: components = 1; break; + case GL_RG: components = 2; break; + case GL_RGB: + case GL_BGR: components = 3; break; + case GL_RGBA: + case GL_BGRA: components = 4; break; + case GL_DEPTH_COMPONENT: components = 1; break; + case GL_DEPTH_STENCIL: components = 2; break; + default: components = 0; break; + } + size_t typeSize = 0; + switch (type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: typeSize = 1; break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: typeSize = 2; break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: typeSize = 4; break; + default: typeSize = 0; break; + } + bytesPerPixel = components * typeSize; + break; + } + } + size_t totalSize = static_cast(width) * static_cast(height) * bytesPerPixel; + MG_Util::Debug::LogD("MG_State: Texture: CalculatePixelDataSize returns %zu", totalSize); + return totalSize; +} + +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); + if (validity != GL_NO_ERROR) { + MG_Util::Debug::LogE("MG_State: Texture: Upload2D CheckUploadingTexture2DValidity 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 || + target == GL_PROXY_TEXTURE_RECTANGLE || target == GL_PROXY_TEXTURE_2D_MULTISAMPLE || + target == GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY); + if (isProxyTexture) { + MG_Util::Debug::LogD("MG_State: Texture: Upload2D proxy texture detected"); + TextureObject& proxyTex = proxyTextures[target]; + TextureParams::MipmapLevel mip{}; + mip.width = width; + mip.height = height; + mip.internalFormat = internalFormat; + mip.format = format; + mip.type = type; + proxyTex.generated = true; + proxyTex.target = target; + proxyTex.params.mipmapData[level] = mip; + return GL_NO_ERROR; + } + + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); + TextureObject& tex = textures[boundTex]; + TextureParams::MipmapLevel mip{}; + mip.width = width; + mip.height = height; + mip.internalFormat = 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); + mip.hasData = true; + MG_Util::Debug::LogD("MG_State: Texture: Upload2D uploaded data size=%zu", dataSize); + } 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; +} + +GLenum TextureState::UpdateRegion2D(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: 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; + } + + 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 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); + 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); + 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); + 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: UpdateRegion2D format mismatch with internal format"); + return GL_INVALID_OPERATION; + } + + mip.hasData = true; + MG_Util::Debug::LogD("MG_State: Texture: UpdateRegion2D validated subimage update"); + + tex.data = data; + + 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()) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (MG_Constants::Texture::VALID_TEXTURE_PARAM_NAMES.find(pname) == MG_Constants::Texture::VALID_TEXTURE_PARAM_NAMES.end()) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat invalid pname=0x%x", pname); + return GL_INVALID_ENUM; + } + + switch (pname) { + case GL_TEXTURE_BORDER_COLOR: + break; + case GL_TEXTURE_MIN_LOD: + if(param < -1000.0f){ + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat param for GL_TEXTURE_MIN_LOD too low: %f", param); + return GL_INVALID_VALUE; + } + break; + case GL_TEXTURE_MAX_LOD: + if(param > 1000.0f){ + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat param for GL_TEXTURE_MAX_LOD too high: %f", param); + return GL_INVALID_VALUE; + } + break; + case GL_TEXTURE_LOD_BIAS: + if(param < -1000.0f || param > 1000.0f) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat param for GL_TEXTURE_LOD_BIAS out of range: %f", param); + return GL_INVALID_VALUE; + } + break; + case GL_TEXTURE_SWIZZLE_R: + case GL_TEXTURE_SWIZZLE_G: + case GL_TEXTURE_SWIZZLE_B: + case GL_TEXTURE_SWIZZLE_A: + if (param != GL_ZERO && param != GL_ONE && param != GL_RED && param != GL_GREEN && param != GL_BLUE && param != GL_ALPHA) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyFloat invalid swizzle param=%f", param); + return GL_INVALID_ENUM; + } + break; + default: + break; + } + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); + TextureObject& tex = textures[boundTex]; + tex.params.texPropertiesFloat[pname] = param; + MG_Util::Debug::LogD("MG_State: Texture: SetTexturePropertyFloat succeeded for texture %u", boundTex); + return GL_NO_ERROR; +} + +GLenum TextureState::SetTexturePropertyInt(GLenum target, GLenum pname, GLint param) { + MG_Util::Debug::LogD("MG_State: Texture: SetTexturePropertyInt called target=0x%x, pname=0x%x, param=%d", target, pname, param); + if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyInt invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (MG_Constants::Texture::VALID_TEXTURE_PARAM_NAMES.find(pname) == MG_Constants::Texture::VALID_TEXTURE_PARAM_NAMES.end()) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyInt invalid pname=0x%x", pname); + return GL_INVALID_ENUM; + } + + switch (pname) { + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MAG_FILTER: + if (param != GL_NEAREST && param != GL_LINEAR && param != GL_NEAREST_MIPMAP_NEAREST && + param != GL_LINEAR_MIPMAP_NEAREST && param != GL_NEAREST_MIPMAP_LINEAR && + param != GL_LINEAR_MIPMAP_LINEAR) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyInt invalid filter param=%d", param); + return GL_INVALID_ENUM; + } + break; + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + case GL_TEXTURE_WRAP_R: + if (param != GL_CLAMP_TO_EDGE && param != GL_CLAMP_TO_BORDER && + param != GL_MIRRORED_REPEAT && param != GL_REPEAT) { + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyInt invalid wrap param=%d", param); + return GL_INVALID_ENUM; + } + break; + case GL_TEXTURE_SWIZZLE_R: + case GL_TEXTURE_SWIZZLE_G: + case GL_TEXTURE_SWIZZLE_B: + case GL_TEXTURE_SWIZZLE_A: + if(param != GL_ZERO && param != GL_ONE && param != GL_RED && param != GL_GREEN && param != GL_BLUE && param != GL_ALPHA){ + MG_Util::Debug::LogE("MG_State: Texture: SetTexturePropertyInt invalid swizzle param=%d", param); + return GL_INVALID_ENUM; + } + break; + default: + break; + } + + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); + TextureObject& tex = textures[boundTex]; + tex.params.texPropertiesInt[pname] = param; + MG_Util::Debug::LogD("MG_State: Texture: SetTexturePropertyInt succeeded for texture %u", boundTex); + return GL_NO_ERROR; +} + +GLenum TextureState::Delete(GLuint texture) { + MG_Util::Debug::LogD("MG_State: Texture: Delete called for texture=%u", texture); + if (!texture) { + MG_Util::Debug::LogE("MG_State: Texture: Delete received texture 0"); + return GL_INVALID_VALUE; + } + + auto it = this->textures.find(texture); + if (it != this->textures.end()) { + InvalidateTextureInAllUnits(texture); + this->textures.erase(it); + freeIDs.insert(texture); + MG_Util::Debug::LogD("MG_State: Texture: Delete succeeded for texture=%u", texture); + } else { + MG_Util::Debug::LogW("MG_State: Texture: Delete texture %u not found", texture); + } + return GL_NO_ERROR; +} + +GLenum TextureState::DeleteN(GLsizei n, const GLuint* textures) { + MG_Util::Debug::LogD("MG_State: Texture: DeleteN called with n=%d", n); + if (n < 1) { + MG_Util::Debug::LogE("MG_State: Texture: DeleteN invalid n=%d", n); + return GL_INVALID_VALUE; + } + + for (GLsizei i = 0; i < n; ++i) { + GLuint id = textures[i]; + if (id == 0) continue; + auto it = this->textures.find(id); + if (it != this->textures.end()) { + InvalidateTextureInAllUnits(id); + this->textures.erase(it); + freeIDs.insert(id); + MG_Util::Debug::LogD("MG_State: Texture: DeleteN deleted texture=%u", id); + } else { + MG_Util::Debug::LogW("MG_State: Texture: DeleteN texture %u not found", id); + } + } + + return GL_NO_ERROR; +} + + +GLenum TextureState::GetLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params) { + MG_Util::Debug::LogD("MG_State: Texture: GetLevelPropertyIntVector called target=0x%x, level=%d, pname=0x%x", target, level, pname); + if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (level < 0) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector invalid level=%d", level); + return GL_INVALID_VALUE; + } + + if (MG_Constants::Texture::VALID_QUERY_LEVEL_PROPERTY_PARAM_NAMES.find(pname) == MG_Constants::Texture::VALID_QUERY_LEVEL_PROPERTY_PARAM_NAMES.end()) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector invalid pname=0x%x", pname); + return GL_INVALID_ENUM; + } + + if ((target == GL_TEXTURE_RECTANGLE || target == GL_PROXY_TEXTURE_RECTANGLE) && level != 0) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector invalid level for rectangle texture"); + return GL_INVALID_VALUE; + } + + GLuint activeUnit = activeTextureUnit; + if (activeUnit >= textureUnits.size()) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector active texture unit out of range"); + return GL_INVALID_OPERATION; + } + + 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 || + target == GL_PROXY_TEXTURE_RECTANGLE || target == GL_PROXY_TEXTURE_2D_MULTISAMPLE || + target == GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY); + + TextureUnitState& unit = textureUnits[activeUnit]; + GLuint boundTexture = unit.GetBoundTexture(target); + + if (!isProxyTexture && boundTexture == 0) { + switch (pname) { + case GL_TEXTURE_COMPRESSED_IMAGE_SIZE: + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector no texture bound for compressed image size"); + return GL_INVALID_OPERATION; + case GL_TEXTURE_INTERNAL_FORMAT: + *params = GL_NONE; + break; + case GL_TEXTURE_COMPRESSED: + *params = GL_FALSE; + break; + default: + *params = 0; + break; + } + MG_Util::Debug::LogD("MG_State: Texture: GetLevelPropertyIntVector returns default value for unbound texture"); + return GL_NO_ERROR; + } + + std::unordered_map::iterator texIt; + + if (!isProxyTexture) { + texIt = textures.find(boundTexture); + if (texIt == textures.end()) { + *params = 0; + MG_Util::Debug::LogW( + "MG_State: Texture: GetLevelPropertyIntVector texture %u not found", + boundTexture); + return GL_NO_ERROR; + } + } + + TextureObject& tex = isProxyTexture ? proxyTextures[target] : texIt->second; + + if (!isProxyTexture && tex.target != target && tex.target != GL_NONE) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector texture target mismatch: texture target=0x%x, expected=0x%x", tex.target, target); + return GL_INVALID_OPERATION; + } + + auto mipIt = tex.params.mipmapData.find(level); + if (mipIt == tex.params.mipmapData.end()) { + switch (pname) { + case GL_TEXTURE_COMPRESSED_IMAGE_SIZE: + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector no mipmap level %d for compressed image size", level); + return GL_INVALID_OPERATION; + case GL_TEXTURE_INTERNAL_FORMAT: + *params = GL_NONE; + break; + case GL_TEXTURE_COMPRESSED: + *params = GL_FALSE; + break; + default: + *params = 0; + break; + } + MG_Util::Debug::LogD("MG_State: Texture: GetLevelPropertyIntVector returns default value for missing mipmap level"); + return GL_NO_ERROR; + } + + const TextureParams::MipmapLevel& mip = mipIt->second; + ComponentSizes sizes = GetComponentSizes(mip.internalFormat); + + switch (pname) { + case GL_TEXTURE_WIDTH: + *params = mip.width; + break; + case GL_TEXTURE_HEIGHT: + *params = mip.height; + break; + case GL_TEXTURE_DEPTH: + *params = 1; // Assuming 2D Texture + break; + case GL_TEXTURE_INTERNAL_FORMAT: + *params = mip.internalFormat; + break; + case GL_TEXTURE_RED_SIZE: + case GL_TEXTURE_GREEN_SIZE: + case GL_TEXTURE_BLUE_SIZE: + case GL_TEXTURE_ALPHA_SIZE: + case GL_TEXTURE_DEPTH_SIZE: + case GL_TEXTURE_STENCIL_SIZE: + switch (pname) { + case GL_TEXTURE_RED_SIZE: *params = sizes.red; break; + case GL_TEXTURE_GREEN_SIZE: *params = sizes.green; break; + case GL_TEXTURE_BLUE_SIZE: *params = sizes.blue; break; + case GL_TEXTURE_ALPHA_SIZE: *params = sizes.alpha; break; + case GL_TEXTURE_DEPTH_SIZE: *params = sizes.depth; break; + case GL_TEXTURE_STENCIL_SIZE: *params = sizes.stencil; break; + default: break; + } + break; + case GL_TEXTURE_COMPRESSED: + *params = sizes.isCompressed ? GL_TRUE : GL_FALSE; + break; + case GL_TEXTURE_COMPRESSED_IMAGE_SIZE: + if (!sizes.isCompressed) { + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector texture is not compressed"); + return GL_INVALID_OPERATION; + } + *params = static_cast(mip.pixelData.size()); + break; + default: + MG_Util::Debug::LogE("MG_State: Texture: GetLevelPropertyIntVector invalid pname=0x%x", pname); + return GL_INVALID_ENUM; + } + MG_Util::Debug::LogD("MG_State: Texture: GetLevelPropertyIntVector returns %d for pname=0x%x", *params, pname); + return GL_NO_ERROR; +} + +void TextureState::InvalidateTextureInAllUnits(GLuint texture) { + MG_Util::Debug::LogD("MG_State: Texture: InvalidateTextureInAllUnits called for texture=%u", texture); + for (auto& unit : textureUnits) { + for (auto& [target, tex] : unit.boundTextures) { + if (tex == texture) { + tex = 0; + MG_Util::Debug::LogD("MG_State: Texture: InvalidateTextureInAllUnits unset texture for target=0x%x", target); + } + } + } +} + +GLenum TextureState::CheckUploadingTexture2DValidity(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: CheckUploadingTexture2DValidity called target=0x%x, level=%d", target, level); + if (MG_Constants::Texture::VALID_TARGETS.find(target) == MG_Constants::Texture::VALID_TARGETS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid target=0x%x", target); + return GL_INVALID_ENUM; + } + + if (level < 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid level=%d", level); + return GL_INVALID_VALUE; + } + + if ((target == GL_TEXTURE_RECTANGLE || target == GL_PROXY_TEXTURE_RECTANGLE) && level != 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid level for rectangle texture"); + return GL_INVALID_VALUE; + } + + if (width < 0 || height < 0) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid dimensions width=%d, height=%d", width, height); + return GL_INVALID_VALUE; + } + + if ((target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) && width != height) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity cube map requires equal width and height"); + return GL_INVALID_ENUM; + } + + if (width > MG_Constants::Texture::MAX_TEXTURE_SIZE) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity width exceeds maximum: %d", width); + return GL_INVALID_VALUE; + } + + if (target == GL_TEXTURE_1D_ARRAY || target == GL_PROXY_TEXTURE_1D_ARRAY) { + if (height > MG_Constants::Texture::MAX_ARRAY_LAYERS) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity height exceeds MAX_ARRAY_LAYERS: %d", height); + return GL_INVALID_VALUE; + } + } else if (height > MG_Constants::Texture::MAX_TEXTURE_SIZE) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity height exceeds maximum: %d", height); + return GL_INVALID_VALUE; + } + + if (MG_Constants::Texture::VALID_INTERNAL_FORMATS.find(internalFormat) == MG_Constants::Texture::VALID_INTERNAL_FORMATS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid internalFormat=0x%x", internalFormat); + return GL_INVALID_VALUE; + } + + if (MG_Constants::Texture::VALID_FORMATS.find(format) == MG_Constants::Texture::VALID_FORMATS.end()) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity 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: CheckUploadingTexture2DValidity invalid type=0x%x", type); + return GL_INVALID_ENUM; + } + + bool typeNeedsRGB = (type == GL_UNSIGNED_BYTE_3_3_2 || type == GL_UNSIGNED_SHORT_5_6_5); + if (typeNeedsRGB && format != GL_RGB) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity type requires RGB but format=0x%x", format); + return GL_INVALID_OPERATION; + } + + bool typeNeedsRGBA = (type == GL_UNSIGNED_SHORT_4_4_4_4 || type == GL_UNSIGNED_INT_8_8_8_8); + if (typeNeedsRGBA && format != GL_RGBA && format != GL_BGRA) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity type requires RGBA but format=0x%x", format); + return GL_INVALID_OPERATION; + } + + bool isDepthInternal = (internalFormat == GL_DEPTH_COMPONENT32F || internalFormat == GL_DEPTH_COMPONENT24 + || internalFormat == GL_DEPTH_COMPONENT16 || internalFormat == GL_DEPTH32F_STENCIL8 + || internalFormat == GL_DEPTH24_STENCIL8 || internalFormat == GL_DEPTH_COMPONENT + || internalFormat == GL_DEPTH_STENCIL); + bool isDepthFormat = (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL); + if (isDepthInternal != isDepthFormat) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity depth internal/format mismatch"); + return GL_INVALID_OPERATION; + } + + if (isDepthInternal && (target != GL_TEXTURE_2D && target != GL_PROXY_TEXTURE_2D && + target != GL_TEXTURE_RECTANGLE && target != GL_PROXY_TEXTURE_RECTANGLE)) { + MG_Util::Debug::LogE("MG_State: Texture: CheckUploadingTexture2DValidity invalid target for depth texture"); + return GL_INVALID_OPERATION; + } + + 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 || + target == GL_PROXY_TEXTURE_RECTANGLE || target == GL_PROXY_TEXTURE_2D_MULTISAMPLE || + target == GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY); + if (!isProxyTexture) { + GLuint boundTex = textureUnits[activeTextureUnit].GetBoundTexture(target); + if (boundTex == 0) { + MG_Util::Debug::LogE( + "MG_State: Texture: CheckUploadingTexture2DValidity no texture bound for target=0x%x", + target); + return GL_INVALID_OPERATION; + } + + TextureObject &tex = textures[boundTex]; + if (tex.target != target) { + MG_Util::Debug::LogE( + "MG_State: Texture: CheckUploadingTexture2DValidity texture target mismatch: texture target=0x%x, expected=0x%x", + tex.target, target); + return GL_INVALID_OPERATION; + } + + if (tex.IsImmutable()) { + MG_Util::Debug::LogE( + "MG_State: Texture: CheckUploadingTexture2DValidity texture is immutable"); + return GL_INVALID_OPERATION; + } + } + + MG_Util::Debug::LogD("MG_State: Texture: CheckUploadingTexture2DValidity succeeded"); + 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}; + switch (internalFormat) { + // Base formats + case GL_R8: + case GL_R8_SNORM: sizes.red = 8; break; + case GL_R16: + case GL_R16_SNORM: + case GL_R16F: sizes.red = 16; break; + case GL_R32F: sizes.red = 32; break; + case GL_R8I: + case GL_R8UI: sizes.red = 8; break; + case GL_R16I: + case GL_R16UI: sizes.red = 16; break; + case GL_R32I: + case GL_R32UI: sizes.red = 32; break; + + case GL_RG8: + case GL_RG8_SNORM: sizes.red = 8; sizes.green = 8; break; + case GL_RG16: + case GL_RG16_SNORM: + case GL_RG16F: sizes.red = 16; sizes.green = 16; break; + case GL_RG32F: sizes.red = 32; sizes.green = 32; break; + case GL_RG8I: + case GL_RG8UI: sizes.red = 8; sizes.green = 8; break; + case GL_RG16I: + case GL_RG16UI: sizes.red = 16; sizes.green = 16; break; + case GL_RG32I: + case GL_RG32UI: sizes.red = 32; sizes.green = 32; break; + + case GL_RGB8: + case GL_RGB8_SNORM: sizes.red = 8; sizes.green = 8; sizes.blue = 8; break; + case GL_RGB16: + case GL_RGB16_SNORM: + case GL_RGB16F: sizes.red = 16; sizes.green = 16; sizes.blue = 16; break; + case GL_RGB32F: sizes.red = 32; sizes.green = 32; sizes.blue = 32; break; + case GL_RGB8I: + case GL_RGB8UI: sizes.red = 8; sizes.green = 8; sizes.blue = 8; break; + case GL_RGB16I: + case GL_RGB16UI: sizes.red = 16; sizes.green = 16; sizes.blue = 16; break; + case GL_RGB32I: + case GL_RGB32UI: sizes.red = 32; sizes.green = 32; sizes.blue = 32; break; + + case GL_RGBA8: + case GL_RGBA8_SNORM: sizes.red = 8; sizes.green = 8; sizes.blue = 8; sizes.alpha = 8; break; + case GL_RGBA16: + case GL_RGBA16_SNORM: + case GL_RGBA16F: sizes.red = 16; sizes.green = 16; sizes.blue = 16; sizes.alpha = 16; break; + case GL_RGBA32F: sizes.red = 32; sizes.green = 32; sizes.blue = 32; sizes.alpha = 32; break; + case GL_RGBA8I: + case GL_RGBA8UI: sizes.red = 8; sizes.green = 8; sizes.blue = 8; sizes.alpha = 8; break; + case GL_RGBA16I: + case GL_RGBA16UI: sizes.red = 16; sizes.green = 16; sizes.blue = 16; sizes.alpha = 16; break; + case GL_RGBA32I: + case GL_RGBA32UI: sizes.red = 32; sizes.green = 32; sizes.blue = 32; sizes.alpha = 32; break; + + // Packed formats + case GL_RGB565: sizes.red = 5; sizes.green = 6; sizes.blue = 5; break; + case GL_RGBA4: sizes.red = 4; sizes.green = 4; sizes.blue = 4; sizes.alpha = 4; break; + case GL_RGB5_A1: sizes.red = 5; sizes.green = 5; sizes.blue = 5; sizes.alpha = 1; break; + case GL_RGB10_A2: + case GL_RGB10_A2UI: sizes.red = 10; sizes.green = 10; sizes.blue = 10; sizes.alpha = 2; break; + case GL_R11F_G11F_B10F: sizes.red = 11; sizes.green = 11; sizes.blue = 10; break; + case GL_RGB9_E5: sizes.red = 9; sizes.green = 9; sizes.blue = 9; break; // shared exponent + + // sRGB formats + case GL_SRGB8: sizes.red = 8; sizes.green = 8; sizes.blue = 8; break; + case GL_SRGB8_ALPHA8: sizes.red = 8; sizes.green = 8; sizes.blue = 8; sizes.alpha = 8; break; + + // Depth/stencil formats + case GL_DEPTH_COMPONENT16: sizes.depth = 16; break; + case GL_DEPTH_COMPONENT24: sizes.depth = 24; break; + case GL_DEPTH_COMPONENT32: + case GL_DEPTH_COMPONENT32F: sizes.depth = 32; break; + case GL_DEPTH24_STENCIL8: sizes.depth = 24; sizes.stencil = 8; break; + case GL_DEPTH32F_STENCIL8: sizes.depth = 32; sizes.stencil = 8; break; + + // Compressed formats + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + sizes.isCompressed = true; + sizes.red = 5; sizes.green = 6; sizes.blue = 5; + if (internalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) sizes.alpha = 1; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RG_RGTC2: + case GL_COMPRESSED_SIGNED_RG_RGTC2: + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_SIGNED_RED_RGTC1: + sizes.isCompressed = true; + break; + + default: + if (internalFormat == GL_RGBA || internalFormat == GL_BGRA || + internalFormat == GL_RGBA_INTEGER || internalFormat == GL_BGRA_INTEGER) { + // Typically matches RGBA8 when no size is specified + sizes.red = 8; sizes.green = 8; sizes.blue = 8; sizes.alpha = 8; + } else if (internalFormat == GL_RGB || internalFormat == GL_BGR || + internalFormat == GL_RGB_INTEGER || internalFormat == GL_BGR_INTEGER) { + sizes.red = 8; sizes.green = 8; sizes.blue = 8; + } else if (internalFormat == GL_RG || internalFormat == GL_RG_INTEGER) { + sizes.red = 8; sizes.green = 8; + } else if (internalFormat == GL_RED || internalFormat == GL_RED_INTEGER) { + sizes.red = 8; + } else if (internalFormat == GL_DEPTH_COMPONENT) { + sizes.depth = 32; // Common default + } else if (internalFormat == GL_DEPTH_STENCIL) { + sizes.depth = 32; sizes.stencil = 8; + } + break; + } + MG_Util::Debug::LogD("MG_State: Texture: GetComponentSizes returns (red=%d, green=%d, blue=%d, alpha=%d, depth=%d, stencil=%d, isCompressed=%d)", + sizes.red, sizes.green, sizes.blue, sizes.alpha, sizes.depth, sizes.stencil, sizes.isCompressed); + return sizes; +} diff --git a/MG/MG_GL/State/Texture/TextureState.h b/MG/MG_GL/State/Texture/TextureState.h new file mode 100644 index 00000000..d5790ce8 --- /dev/null +++ b/MG/MG_GL/State/Texture/TextureState.h @@ -0,0 +1,98 @@ +// +// Created by BZLZHH on 2025/4/4. +// + +#ifndef MOBILEGL_TEXTURESTATE_H +#define MOBILEGL_TEXTURESTATE_H +#define MOBILEGL_GLSTATE_H + +#include "../../../Includes.h" + +struct ComponentSizes { + GLint red = 0; + GLint green = 0; + GLint blue = 0; + GLint alpha = 0; + GLint depth = 0; + GLint stencil = 0; + bool isCompressed = false; +}; + +struct TextureParams { + std::unordered_map texPropertiesFloat; + std::unordered_map texPropertiesInt; + struct MipmapLevel { + GLsizei width = 0; + GLsizei height = 0; + GLenum format = GL_RGBA; + GLenum internalFormat = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; + std::vector pixelData; + bool hasData = false; + }; + std::unordered_map mipmapData; +}; + +class TextureObject { +public: + bool generated = false; + GLenum target = GL_NONE; + TextureParams params; + const void* data = nullptr; + bool IsImmutable() const; +}; + +class TextureUnitState { +private: + GLenum activeTarget = GL_TEXTURE_2D; + +public: + std::unordered_map boundTextures; + void Bind(GLenum target, GLuint texture); + GLuint GetBoundTexture(GLenum target); +}; + +class TextureState { +private: + std::vector textureUnits; + GLuint activeTextureUnit = 0; + + GLuint lastUsedID = 0; + std::set freeIDs; + + std::unordered_map textures; + std::unordered_map proxyTextures; +public: + TextureState(); + + static TextureState& GetInstance(); + bool IsTextureGenerated(GLuint texture); + bool IsTexture(GLuint texture); + + // Return: the validity of the operation, according to OpenGL 3 standard + GLenum BindUnit(GLenum textureUnit); + GLenum Create(GLuint* texture); + GLenum CreateN(GLsizei n, GLuint* textures); + GLenum Bind(GLenum target, GLuint texture); + GLenum Upload2D(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const void* data); + GLenum UpdateRegion2D(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, GLenum format, + GLenum type, const GLvoid* data); + GLenum SetTexturePropertyInt(GLenum target, GLenum pname, GLint param); + GLenum SetTexturePropertyFloat(GLenum target, GLenum pname, GLfloat param); + GLenum Delete(GLuint texture); + GLenum DeleteN(GLsizei n, const GLuint* textures); + GLenum GetLevelPropertyIntVector(GLenum target, GLint level, GLenum pname, GLint* params); + +private: + static static size_t CalculatePixelDataSize(GLenum format, GLenum type, GLsizei width, GLsizei height); + void InvalidateTextureInAllUnits(GLuint texture); + static ComponentSizes GetComponentSizes(GLenum internalFormat); + GLenum CheckUploadingTexture2DValidity(GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const void* data); +}; + +#endif //MOBILEGL_TEXTURESTATE_H