#include "DirectGLES.h" #include "Utils.h" #include "Managers.h" #include #include #include #include #include #include namespace MobileGL::MG_Backend::DirectGLES { // TODO: deletion of deleted objects namespace BufferImpl { void SyncNeccessaryBuffers() { // All buffers we need are: // 1.VBOs 2.IBO 3.UBOs (TODO) 4.SSBOs (TODO) Vector> buffersToSync; const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); if (!currentVAOObject) { MGLOG_E("No VAO is currently bound, cannot sync necessary buffers."); return; } for (const auto& attrib : currentVAOObject->GetAllAttributes()) { if (!attrib.Enabled) continue; const auto& bufferObject = attrib.Buffer; if (bufferObject) { buffersToSync.push_back(bufferObject); } } const auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); if (possibleIBO) { buffersToSync.push_back(possibleIBO); } const auto& pbo = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); if (pbo) { buffersToSync.push_back(pbo); } // Do real sync for (auto& bufferObject : buffersToSync) { const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); SharedPtr backendBufferObject; if (backendBufferIt == g_backendBufferObjects.end()) { backendBufferObject = MakeShared(); g_backendBufferObjects[bufferObject] = backendBufferObject; } else { backendBufferObject = backendBufferIt->second; } backendBufferObject->SyncToBackend(bufferObject); } } } // namespace BufferImpl namespace VertexArrayImpl { void SyncCurrentVAO() { auto currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); if (!currentVAOObject) { MGLOG_E("No VAO is currently bound, cannot sync current VAO."); return; } const auto& backendVAOIt = g_backendVertexArrayObjects.find(currentVAOObject); SharedPtr backendVAOObject; if (backendVAOIt == g_backendVertexArrayObjects.end()) { backendVAOObject = MakeShared(); g_backendVertexArrayObjects[currentVAOObject] = backendVAOObject; } else { backendVAOObject = backendVAOIt->second; } backendVAOObject->SyncToBackend(currentVAOObject); } } // namespace VertexArrayImpl namespace TextureImpl { void SyncNeccessaryTextures() { // All textures we need are: // 1. textures bound to texture units (TODO: only sync ones that are used in current program) // 2. textures used in current FBO // 3. textures bound to image units (TODO) Vector> texturesToSync; for (int index = 0; index < MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; ++index) { auto& unit = MG_State::pGLContext->GetTextureUnitObject(index); for (const auto& bindingSlot : unit.GetAllBindingSlots()) { const auto& textureObject = bindingSlot.GetBoundObject(); if (textureObject) { texturesToSync.push_back(textureObject); } } } const auto& currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); if (currentFBO) { for (const auto& attachment : currentFBO->GetAllAttachments()) { if (!attachment.IsTexture()) continue; const auto& textureObject = attachment.GetTexture(); if (textureObject) { texturesToSync.push_back(textureObject); } } } // Do real sync for (auto& textureObject : texturesToSync) { const auto& backendTextureIt = g_backendTextureObjects.find(textureObject); SharedPtr backendTextureObject; if (backendTextureIt == g_backendTextureObjects.end()) { backendTextureObject = MakeShared(); g_backendTextureObjects[textureObject] = backendTextureObject; } else { backendTextureObject = backendTextureIt->second; } backendTextureObject->SyncToBackend(textureObject); } } } // namespace TextureImpl namespace FramebufferImpl { void SyncCurrentFBO() { auto currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); if (!currentFBO) { MGLOG_E("No FBO is currently bound, cannot sync current FBO."); return; } if (currentFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { // Default FBO, nothing to sync return; } const auto& backendFBOIt = g_backendFramebufferObjects.find(currentFBO); SharedPtr backendFBOObject; if (backendFBOIt == g_backendFramebufferObjects.end()) { backendFBOObject = MakeShared(); g_backendFramebufferObjects[currentFBO] = backendFBOObject; } else { backendFBOObject = backendFBOIt->second; } backendFBOObject->SyncToBackend(currentFBO); } } // namespace FramebufferImpl namespace RenderStateImpl { void SyncRenderState() { MG_External::GLES::glViewport( MG_State::pGLContext->GetViewport().x(), MG_State::pGLContext->GetViewport().y(), MG_State::pGLContext->GetViewport().z(), MG_State::pGLContext->GetViewport().w()); #define SYNC_CAPABILITY(cap_mg, cap_gl) \ if (MG_State::pGLContext->IsCapabilityEnabled(cap_mg)) { \ MG_External::GLES::glEnable(cap_gl); \ } else { \ MG_External::GLES::glDisable(cap_gl); \ } SYNC_CAPABILITY(CapabilityInput::Blend, GL_BLEND); SYNC_CAPABILITY(CapabilityInput::DepthTest, GL_DEPTH_TEST); SYNC_CAPABILITY(CapabilityInput::ScissorTest, GL_SCISSOR_TEST); SYNC_CAPABILITY(CapabilityInput::CullFace, GL_CULL_FACE); #undef SYNC_CAPABILITY const auto& ToGLBoolean = [](Bool b) -> GLboolean { return b ? GL_TRUE : GL_FALSE; }; { // Blend func BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha; MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); MG_External::GLES::glBlendFuncSeparate( MG_Util::ConvertBlendFactorToGLEnum(srcRGB), MG_Util::ConvertBlendFactorToGLEnum(dstRGB), MG_Util::ConvertBlendFactorToGLEnum(srcAlpha), MG_Util::ConvertBlendFactorToGLEnum(dstAlpha)); } { // Blend equation DepthTestFunc df = MG_State::pGLContext->GetDepthFunc(); MG_External::GLES::glDepthFunc(MG_Util::ConvertDepthTestFuncToGLEnum(df)); MG_External::GLES::glDepthMask(MG_State::pGLContext->GetDepthMask() ? GL_TRUE : GL_FALSE); } { // Color mask BoolVec4 colorMask = MG_State::pGLContext->GetColorMask(); MG_External::GLES::glColorMask(ToGLBoolean(colorMask.x()), ToGLBoolean(colorMask.y()), ToGLBoolean(colorMask.z()), ToGLBoolean(colorMask.w())); } { // Clear values const FloatVec4& clearCol = MG_State::pGLContext->GetClearColor(); MG_External::GLES::glClearColor(clearCol.x(), clearCol.y(), clearCol.z(), clearCol.w()); MG_External::GLES::glClearDepthf(MG_State::pGLContext->GetClearDepth()); } { // Cull face mode CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode(); MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm)); } { // Scissor box const IntVec4& scissorBox = MG_State::pGLContext->GetScissorBox(); MG_External::GLES::glScissor(scissorBox.x(), scissorBox.y(), scissorBox.z(), scissorBox.w()); } } } // namespace RenderStateImpl namespace PrgramImpl { void SyncCurrentProgram() { auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); if (!currentProgram) { MG_External::GLES::glUseProgram(0); return; } const auto& backendProgramIt = g_backendProgramObjects.find(currentProgram); SharedPtr backendProgram; if (backendProgramIt == g_backendProgramObjects.end()) { backendProgram = MakeShared(); g_backendProgramObjects[currentProgram] = backendProgram; backendProgram->SyncToBackend(currentProgram); } else { backendProgram = backendProgramIt->second; if (!backendProgram->GetBackendProgramId()) { backendProgram->SyncToBackend(currentProgram); } } } } // namespace PrgramImpl void BindCurrentFBO(FramebufferTarget target) { const auto& currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(target).GetBoundObject(); if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { const auto& backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find(currentFBO); if (backendFBOIt != FramebufferImpl::g_backendFramebufferObjects.end()) { backendFBOIt->second->Bind(target); } } else { if (target == FramebufferTarget::Read) MG_External::GLES::glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); else MG_External::GLES::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } } void PrepareForDraw() { BufferImpl::SyncNeccessaryBuffers(); VertexArrayImpl::SyncCurrentVAO(); TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); PrgramImpl::SyncCurrentProgram(); RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray(); if (currentVAO) { const auto& backendVAOIt = VertexArrayImpl::g_backendVertexArrayObjects.find(currentVAO); if (backendVAOIt != VertexArrayImpl::g_backendVertexArrayObjects.end()) { backendVAOIt->second->Bind(); } } else { MG_External::GLES::glBindVertexArray(0); } Int maxTextureUnits = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; for (Int unit = 0; unit < maxTextureUnits; ++unit) { auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); if (!textureUnit.GetBindingSlot(TextureTarget::Texture2D).GetBoundObject()) continue; // TODO MG_External::GLES::glActiveTexture(GL_TEXTURE0 + unit); for (const auto& bindingSlot : textureUnit.GetAllBindingSlots()) { const auto& textureObject = bindingSlot.GetBoundObject(); if (!textureObject) continue; const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue; GLenum target = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); backendTextureIt->second->Bind(target); const auto& samplerObj = textureObject->GetSamplerObject(); const auto minfilter = samplerObj->GetMinFilter(); MG_External::GLES::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, MG_Util::ConvertSamplerFilterModeToGLEnum(minfilter)); const auto magfilter = samplerObj->GetMagFilter(); MG_External::GLES::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, MG_Util::ConvertSamplerFilterModeToGLEnum(magfilter)); MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MIN_LOD, samplerObj->GetMinLod()); MG_External::GLES::glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerObj->GetMaxLod()); } } Int originalActiveUnit = MG_State::pGLContext->GetActiveTextureUnit(); MG_External::GLES::glActiveTexture(GL_TEXTURE0 + originalActiveUnit); const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); if (currentProgram) { const auto& backendProgramIt = PrgramImpl::g_backendProgramObjects.find(currentProgram); if (backendProgramIt != PrgramImpl::g_backendProgramObjects.end()) { backendProgramIt->second->Use(); // UBO MG_External::GLES::glBindBuffer(GL_UNIFORM_BUFFER, backendProgramIt->second->GetBackendGlobalUBOId()); MG_External::GLES::glBufferSubData(GL_UNIFORM_BUFFER, 0, currentProgram->GetUBOSize(), currentProgram->MapUBO()); MG_External::GLES::glBindBuffer(GL_UNIFORM_BUFFER, 0); Uint blockIndex = MG_External::GLES::glGetUniformBlockIndex( backendProgramIt->second->GetBackendProgramId(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME); MG_External::GLES::glUniformBlockBinding(backendProgramIt->second->GetBackendProgramId(), blockIndex, 0); MG_External::GLES::glBindBufferBase(GL_UNIFORM_BUFFER, 0, backendProgramIt->second->GetBackendGlobalUBOId()); // Sampler unit binding auto maxUniformLoc = currentProgram->GetMaxUniformLocation(); for (int loc = 0; loc < maxUniformLoc; ++loc) { auto unit = currentProgram->GetUniformSamplerOrImageUnitIndex(loc); if (unit == -1) continue; auto& name = currentProgram->GetUniformName(loc); auto locAtBackend = MG_External::GLES::glGetUniformLocation( backendProgramIt->second->GetBackendProgramId(), name.c_str()); MG_External::GLES::glUniform1i(locAtBackend, unit); } } else { MG_External::GLES::glUseProgram(0); } } } void DrawArrays(GLenum mode, GLint first, GLsizei count) { PrepareForDraw(); MG_External::GLES::glDrawArrays(mode, first, count); } void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { PrepareForDraw(); MG_External::GLES::glDrawElements(mode, count, type, indices); } void Clear(GLbitfield mask) { TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); MG_External::GLES::glClear(mask); } void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint baseVertex) { PrepareForDraw(); MG_External::GLES::glDrawElementsBaseVertex(mode, count, type, indices, baseVertex); } void MultiDrawElements(GLenum mode, const GLsizei* counts, GLenum type, const void* const* indices, GLsizei drawcount) { PrepareForDraw(); for (GLsizei i = 0; i < drawcount; ++i) { MG_External::GLES::glDrawElements(mode, counts[i], type, indices[i]); } } void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* counts, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* baseVertices) { PrepareForDraw(); for (GLsizei i = 0; i < drawcount; ++i) { MG_External::GLES::glDrawElementsBaseVertex(mode, counts[i], type, indices[i], baseVertices[i]); } } void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { TextureImpl::SyncNeccessaryTextures(); FramebufferImpl::SyncCurrentFBO(); RenderStateImpl::SyncRenderState(); BindCurrentFBO(FramebufferTarget::Draw); BindCurrentFBO(FramebufferTarget::Read); MG_External::GLES::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } const GLubyte* GetString(GLenum name) { return MG_External::GLES::glGetString(name); } } // namespace MobileGL::MG_Backend::DirectGLES