#pragma once #include #include "base/Types.h" #include "platform/GL.h" #include "UtilsGL.h" #include "renderer/backend/Enums.h" #define AX_ENABLE_STATE_GUARD 1 // Inline opengl state set calls NS_AX_BACKEND_BEGIN struct BlendEquationSeparateState { BlendEquationSeparateState(GLenum rgb, GLenum alpha) : rgbBlendOperation(rgb), alphaBlendOperation(alpha) {} bool equals(GLenum rgb, GLenum alpha) const { return this->rgbBlendOperation == rgb && this->alphaBlendOperation == alpha; } GLenum rgbBlendOperation; GLenum alphaBlendOperation; }; struct BlendFuncSeparateState { BlendFuncSeparateState(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) : sourceRGBBlendFactor(sfactorRGB) , destinationRGBBlendFactor(dfactorRGB) , sourceAlphaBlendFactor(sfactorAlpha) , destinationAlphaBlendFactor(dfactorAlpha) {} inline bool equals(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) const { return this->sourceRGBBlendFactor == sfactorRGB && this->destinationRGBBlendFactor == dfactorRGB && this->sourceAlphaBlendFactor == sfactorAlpha && this->destinationAlphaBlendFactor == dfactorAlpha; } GLenum sourceRGBBlendFactor; GLenum destinationRGBBlendFactor; GLenum sourceAlphaBlendFactor; GLenum destinationAlphaBlendFactor; }; struct ColorMaskState { ColorMaskState(GLboolean r, GLboolean g, GLboolean b, GLboolean a) : writeMaskRed(r), writeMaskGreen(g), writeMaskBlue(b), writeMaskAlpha(a) {} inline bool equals(GLboolean r, GLboolean g, GLboolean b, GLboolean a) const { return this->writeMaskRed == r && this->writeMaskGreen == g && this->writeMaskBlue == b && this->writeMaskAlpha == a; } GLboolean writeMaskRed; GLboolean writeMaskGreen; GLboolean writeMaskBlue; GLboolean writeMaskAlpha; }; struct StencilFuncState { StencilFuncState(GLenum func, GLint ref, GLuint mask) : stencilCompareFunction(func), stencilReferenceValueFront(ref), readMask(mask) {} inline bool equals(GLenum func, GLint ref, GLuint mask) const { return this->stencilCompareFunction == func && this->stencilReferenceValueFront == ref && this->readMask == mask; } unsigned int stencilCompareFunction; int stencilReferenceValueFront; unsigned int readMask; }; struct StencilOperationState { StencilOperationState(GLenum fail, GLenum zfail, GLenum zpass) : stencilFailureOperation(fail), depthFailureOperation(zfail), depthStencilPassOperation(zpass) {} inline bool equals(GLenum fail, GLenum zfail, GLenum zpass) const { return this->stencilFailureOperation == fail && this->depthFailureOperation == zfail && this->depthStencilPassOperation == zpass; } unsigned int stencilFailureOperation; unsigned int depthFailureOperation; unsigned int depthStencilPassOperation; }; struct CommonBindState { CommonBindState(GLenum t, GLuint h) : target(t), handle(h) {} inline bool equals(GLenum t, GLuint h) const { return this->target == t && this->handle == h; } GLenum target; GLuint handle; }; struct UniformBufferBaseBindState { UniformBufferBaseBindState(GLenum i, GLuint h) : index(i), handle(h) {} inline bool equals(GLenum i, GLuint h) const { return this->index == i && this->handle == h; } GLuint index; GLuint handle; }; struct OpenGLState { constexpr static GLenum BufferTargets[] = { GL_ARRAY_BUFFER, // VERTEX of VAO GL_ELEMENT_ARRAY_BUFFER, // INDEX of VAO GL_UNIFORM_BUFFER, // UNIFORM GL_PIXEL_PACK_BUFFER, // PIXEL }; constexpr static int MAX_VERTEX_ATTRIBS = 16; template static inline void try_enable(GLenum target, _Left& opt) { #if defined(AX_ENABLE_STATE_GUARD) if (opt.has_value() && opt.value()) return; opt = true; #endif glEnable(target); } template static inline void try_disable(GLenum target, _Left& opt) { #if defined(AX_ENABLE_STATE_GUARD) if (!opt.has_value() || !opt.value()) return; opt = false; #endif glDisable(target); } template static inline void try_call(_Func&& func, _Left& opt, _Right&& v) { #if defined(AX_ENABLE_STATE_GUARD) if (opt == v) return; opt = v; #endif func(v); } template static inline void try_callf(_Func&& func, _Left& opt, _Right&& v, _Args&&... args) { #if defined(AX_ENABLE_STATE_GUARD) if (opt == v) return; opt = v; #endif func(args...); } template static inline void try_callu(_Func&& func, GLenum target, _Left& opt, _Right&& v) { #if defined(AX_ENABLE_STATE_GUARD) if (opt == v) return; opt = v; #endif func(target, v); } template static inline void try_callx(_Func&& func, _Left& opt, _Args&&... args) { #if defined(AX_ENABLE_STATE_GUARD) if (opt && (*opt).equals(args...)) return; opt.emplace(args...); #endif func(args...); } template static inline void try_callxu(_Func&& func, GLenum upvalue, _Left& opt, _Args&&... args) { #if defined(AX_ENABLE_STATE_GUARD) if (opt && (*opt).equals(args...)) return; opt.emplace(args...); #endif func(upvalue, args...); } using CullMode = backend::CullMode; using Winding = backend::Winding; using UtilsGL = backend::UtilsGL; static void reset(); void viewport(const Viewport& v) { try_callf(glViewport, _viewPort, v, v.x, v.y, v.width, v.height); } void winding(Winding v) { try_callf(glFrontFace, _winding, v, UtilsGL::toGLFrontFace(v)); } void enableDepthTest() { try_enable(GL_DEPTH_TEST, _depthTest); } void disableDepthTest() { try_disable(GL_DEPTH_TEST, _depthTest); } void enableBlend() { try_enable(GL_BLEND, _blend); } void disableBlend() { try_disable(GL_BLEND, _blend); } void enableScissor(GLint x, GLint y, GLsizei width, GLsizei height) { try_enable(GL_SCISSOR_TEST, _scissor); glScissor(x, y, width, height); } void disableScissor() { try_disable(GL_SCISSOR_TEST, _scissor); } void lineWidth(float v) { try_call(glLineWidth, _lineWidth, v); } void bindFrameBuffer(GLuint v) { try_callu(glBindFramebuffer, GL_FRAMEBUFFER, _frameBufferBind, v); } void blendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) { try_callx(glBlendEquationSeparate, _blendEquationSeparate, modeRGB, modeAlpha); } void blendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) { try_callx(glBlendFuncSeparate, _blendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); } void colorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { try_callx(glColorMask, _colorMask, r, g, b, a); } void depthMask(GLboolean v) { try_call(glDepthMask, _depthMask, v); } void depthFunc(GLenum v) { try_call(glDepthFunc, _depthFunc, v); } void enableStencilTest() { try_enable(GL_STENCIL_TEST, _stencilTest); } void disableStencilTest() { try_disable(GL_STENCIL_TEST, _stencilTest); } void enableCullFace(GLenum mode) { try_enable(GL_CULL_FACE, _cullFace); glCullFace(mode); } void disableCullFace() { try_disable(GL_CULL_FACE, _cullFace); } void useProgram(GLuint v) { try_call(glUseProgram, _programBind, v); } void stencilFunc(GLenum func, GLint ref, GLuint mask) { try_callx(glStencilFunc, _stencilFunc, func, ref, mask); } void stencilFuncFront(GLenum func, GLint ref, GLuint mask) { try_callxu(glStencilFuncSeparate, GL_FRONT, _stencilFuncFront, func, ref, mask); } void stencilFuncBack(GLenum func, GLint ref, GLuint mask) { try_callxu(glStencilFuncSeparate, GL_BACK, _stencilFuncBack, func, ref, mask); } void stencilOp(GLenum fail, GLenum zfail, GLenum zpass) { try_callx(glStencilOp, _stencilOp, fail, zfail, zpass); } void stencilOpFront(GLenum fail, GLenum zfail, GLenum zpass) { try_callxu(glStencilOpSeparate, GL_FRONT, _stencilOpFront, fail, zfail, zpass); } void stencilOpBack(GLenum fail, GLenum zfail, GLenum zpass) { try_callxu(glStencilOpSeparate, GL_BACK, _stencilOpBack, fail, zfail, zpass); } void stencilMask(GLuint v) { try_call(glStencilMask, _stencilMask, v); } void stencilMaskFront(GLuint v) { try_callu(glStencilMaskSeparate, GL_FRONT, _stencilMaskFront, v); } void stencilMaskBack(GLuint v) { try_callu(glStencilMaskSeparate, GL_BACK, _stencilMaskBack, v); } void activeTexture(GLenum v) { try_call(glActiveTexture, _activeTexture, v); } void bindTexture(GLenum target, GLuint handle) { try_callx(glBindTexture, _textureBinding, target, handle); } void deleteTexture(GLenum target, GLuint handle) { glDeleteTextures(1, &handle); if (_textureBinding.has_value() && _textureBinding->handle == handle) _textureBinding.reset(); } GLenum bindBuffer(BufferType type, GLuint buffer) { auto target = BufferTargets[static_cast(type)]; try_callu(glBindBuffer, target, _bufferBindings[static_cast(type)], buffer); return target; } void deleteBuffer(BufferType type, GLuint buffer) { glDeleteBuffers(1, &buffer); if (_bufferBindings[static_cast(type)] == buffer) _bufferBindings[static_cast(type)].reset(); } void bindUniformBufferBase(GLuint index, GLuint handle) { try_callxu(glBindBufferBase, GL_UNIFORM_BUFFER, _uniformBufferState, index, handle); } // useful for multi GL context before GL context switch, reset VAO state // VAO not share between context // shareable: texture, uniform buffer // not shareable: FBO, VAO(GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER) void resetVAO() { _bufferBindings[static_cast(BufferType::ARRAY_BUFFER)].reset(); _bufferBindings[static_cast(BufferType::ELEMENT_ARRAY_BUFFER)].reset(); } void enableVertexAttribArray(GLuint index) { const auto mask = 1 << index; if (!(_attribBits & mask)) { glEnableVertexAttribArray(index); _attribBits |= mask; } } void disableVertexAttribArray(GLuint index) { const auto mask = 1 << index; if (_attribBits & mask) { glDisableVertexAttribArray(index); _attribBits &= ~mask; } } void disableUnusedVertexAttribs(uint32_t usedBits) { if (usedBits != _attribBits) { uint32_t unusedEnabledBits = _attribBits & ~usedBits; #if defined(_DEBUG) struct VertexBits { uint16_t bit0 : 1; uint16_t bit1 : 1; uint16_t bit2 : 1; uint16_t bit3 : 1; uint16_t bit4 : 1; uint16_t bit5 : 1; uint16_t bit6 : 1; uint16_t bit7 : 1; uint16_t bit8 : 1; uint16_t bit9 : 1; uint16_t bit10 : 1; uint16_t bit11 : 1; uint16_t bit12 : 1; uint16_t bit13 : 1; uint16_t bit14 : 1; uint16_t bit15 : 1; uint16_t reserved; }; VertexBits *usedBitsDeb = reinterpret_cast(&usedBits), *enabledBitsDeb = reinterpret_cast(&_attribBits), *unusedEnabledBitsDeb = reinterpret_cast(&unusedEnabledBits); #endif for (auto i = 0; unusedEnabledBits && i < MAX_VERTEX_ATTRIBS; ++i) { if (unusedEnabledBits & 1) disableVertexAttribArray(i); unusedEnabledBits >>= 1; } assert(usedBits == _attribBits); } } void setVertexAttribDivisor(GLuint index) { const auto mask = 1 << index; if (!(_divisorBits & mask)) { #if defined(__ANDROID__) && AX_GLES_PROFILE == 200 if (glVertexAttribDivisor) glVertexAttribDivisor(index, 1); #else glVertexAttribDivisor(index, 1); #endif _divisorBits |= mask; } } void clearVertexAttribDivisor(GLuint index) { const auto mask = 1 << index; if (_divisorBits & mask) { #if defined(__ANDROID__) && AX_GLES_PROFILE == 200 if (glVertexAttribDivisor) glVertexAttribDivisor(index, 0); #else glVertexAttribDivisor(index, 0); #endif _divisorBits &= ~mask; } } private: uint32_t _attribBits{0}; // vertexAttribArray bitset uint32_t _divisorBits{0}; // divisor bitset std::optional _bufferBindings[(int)BufferType::COUNT]; std::optional _viewPort; std::optional _winding; std::optional _depthTest; std::optional _blend; std::optional _scissor; std::optional _lineWidth; std::optional _frameBufferBind; std::optional _blendEquationSeparate; std::optional _blendFuncSeparate; std::optional _colorMask; std::optional _depthMask; std::optional _depthFunc; std::optional _stencilTest; std::optional _cullFace; std::optional _programBind; std::optional _stencilFunc; std::optional _stencilFuncFront; std::optional _stencilFuncBack; std::optional _stencilOp; std::optional _stencilOpFront; std::optional _stencilOpBack; std::optional _stencilMask; std::optional _stencilMaskFront; std::optional _stencilMaskBack; std::optional _activeTexture; std::optional _textureBinding; std::optional _uniformBufferState; }; AX_DLL extern OpenGLState* __gl; NS_AX_BACKEND_END