/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// // GLES2 device local context (MacOS) #include "OdaCommon.h" #include "TrGL2LocalContext.h" #include "GLES2Include.h" // Offscreen Objc wrappers void *odGLES2AttachCocoaPB(unsigned width, unsigned height); void odGLES2DetachCocoaPB(void *pObj); void odGLES2MakeCurrentCocoaPB(void *pObj); bool odGLES2CheckDimsCocoaPB(void *pObj, unsigned width, unsigned height); GLuint odGLES2GetFrameBufferInCocoaPB(void *pObj); // Onscreen Objc wrappers void *odGLES2AttachCocoaNS(void *pNSView, void *pGLContext, bool bDoubleBuffer, unsigned width, unsigned height); void odGLES2DetachCocoaNS(void *pObj); void odGLES2MakeCurrentCocoaNS(void *pObj); void *odGLES2ExtractContextCocoaNS(void *pObj); void odGLES2PresentCocoaNS(void *pObj); bool odGLES2CheckDimsCocoaNS(void *pObj, unsigned width, unsigned height); void odGetWndSize(void *pObj, unsigned int& width, unsigned int& height); class OdGLES2LocalContextImpl : public OdTrGL2LocalContext { protected: void *m_pOffscreen; void *m_pOnscreen; GLuint m_externalFBO; bool m_bDoubleBuffer; bool m_bContextCreated; public: OdGLES2LocalContextImpl() : OdTrGL2LocalContext() , m_pOffscreen(NULL) , m_pOnscreen(NULL) , m_externalFBO(0) , m_bDoubleBuffer(false) , m_bContextCreated(false) { } ~OdGLES2LocalContextImpl() { destroyContext(); } void createContext(OdTrVisRenderClient *pDevice) { if (!m_pOffscreen && !m_pOnscreen) { if (pDevice->hasProperty(OD_T("ExternalFBO"))) { m_externalFBO = (GLuint)pDevice->getProperty(OD_T("ExternalFBO"))->getUInt32(); } if ((pDevice->hasProperty(OD_T("CreateContext")) && !pDevice->getProperty(OD_T("CreateContext"))->getBool()) || m_externalFBO) { GLES2_Extensions_Initialize(pDevice); m_bContextCreated = true; return; } // For raster const bool bDirectBuffer = pDevice->hasDirectRenderBuffer(); // Double buffer m_bDoubleBuffer = false; if (!bDirectBuffer && pDevice->hasProperty(OD_T("DoubleBufferEnabled"))) m_bDoubleBuffer = pDevice->getProperty(OD_T("DoubleBufferEnabled"))->getBool(); // Initialize context void *pNSView = NULL, *pGLContext = NULL; if (pDevice->hasProperty(OD_T("NSView"))) pNSView = (void*)pDevice->getProperty(OD_T("NSView"))->getIntPtr(); if (pDevice->hasProperty(OD_T("NSOpenGLContext"))) pGLContext = (void*)pDevice->getProperty(OD_T("NSOpenGLContext"))->getIntPtr(); if (bDirectBuffer) { m_pOffscreen = ::odGLES2AttachCocoaPB((unsigned)pDevice->outputWindowWidth(), (unsigned)pDevice->outputWindowHeight()); if (!m_pOffscreen) pDevice->emitError("Can't initialize offscreen renderer."); m_externalFBO = ::odGLES2GetFrameBufferInCocoaPB(m_pOffscreen); if (pDevice->hasProperty(OD_T("ExternalFBO"))) pDevice->setProperty(OD_T("ExternalFBO"), OdRxVariantValue((OdUInt32)m_externalFBO)); } else { if (!pNSView && !pGLContext) pDevice->emitError("No window specified to attach OpenGL context."); m_pOnscreen = ::odGLES2AttachCocoaNS(pNSView, pGLContext, m_bDoubleBuffer, (unsigned)pDevice->outputWindowWidth(), (unsigned)pDevice->outputWindowHeight()); if (!m_pOnscreen) pDevice->emitError("Can't initialize onscreen renderer."); } // Register context data if (m_pOnscreen && !pGLContext && pDevice->hasProperty(OD_T("NSOpenGLContext"))) pDevice->setProperty(OD_T("NSOpenGLContext"), OdRxVariantValue((OdIntPtr)::odGLES2ExtractContextCocoaNS(m_pOnscreen))); // Run extensions initialization GLES2_Extensions_Initialize(pDevice); } m_bContextCreated = true; } void updateContext(OdTrVisRenderClient *pDevice) { if (m_pOffscreen && !::odGLES2CheckDimsCocoaPB(m_pOffscreen, (unsigned)pDevice->outputWindowWidth(), (unsigned)pDevice->outputWindowHeight())) { if (pDevice->hasProperty(OD_T("SaveContextData"))) // Save OpenGL context data pDevice->setProperty(OD_T("SaveContextData"), OdRxVariantValue(true)); ::odGLES2DetachCocoaPB(m_pOffscreen); m_pOffscreen = ::odGLES2AttachCocoaPB((unsigned)pDevice->outputWindowWidth(), (unsigned)pDevice->outputWindowHeight()); m_externalFBO = ::odGLES2GetFrameBufferInCocoaPB(m_pOffscreen); if (pDevice->hasProperty(OD_T("ExternalFBO"))) pDevice->setProperty(OD_T("ExternalFBO"), OdRxVariantValue((OdUInt32)m_externalFBO)); if (pDevice->hasProperty(OD_T("SaveContextData"))) // Load OpenGL context data pDevice->setProperty(OD_T("SaveContextData"), OdRxVariantValue(false)); } if (m_pOnscreen) ::odGLES2CheckDimsCocoaNS(m_pOnscreen, (unsigned)pDevice->outputWindowWidth(), (unsigned)pDevice->outputWindowHeight()); } void destroyContext() { if (m_pOffscreen) { ::odGLES2DetachCocoaPB(m_pOffscreen); m_pOffscreen = NULL; } if (m_pOnscreen) { ::odGLES2DetachCocoaNS(m_pOnscreen); m_pOnscreen = NULL; } m_externalFBO = 0; m_bContextCreated = false; } bool isContextCreated() const { return m_bContextCreated; } void makeCurrentContext() { if (m_pOffscreen) ::odGLES2MakeCurrentCocoaPB(m_pOffscreen); if (m_pOnscreen) ::odGLES2MakeCurrentCocoaNS(m_pOnscreen); if (m_externalFBO) ::glBindFramebuffer(GL_FRAMEBUFFER, m_externalFBO); } void presentContext() { if (m_pOnscreen) { ::odGLES2PresentCocoaNS(m_pOnscreen); #if 1 if (m_bDoubleBuffer) { // Some video drivers can't provide partial swap and always swaps entire back buffer. But data isn't synchronized between buffers, // and garbage could be visible in this case. This workaround require some time on additional copying, but there is no way to // detect if driver supports partial swap or not. { // Before doing copy we must clear alpha component which can influence onto copied image unpredictably. OdTrRndDrawBufferLayout _backRead(this, DrawBufferLayout(GL_FRONT)); ::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f); ::glClear(GL_COLOR_BUFFER_BIT); ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } OdTrRndDrawBufferLayout _backBuf(this, DrawBufferLayout(GL_BACK)); OdTrRndReadBufferLayout _frontBuf(this, ReadBufferLayout(GL_FRONT)); unsigned int width = 0, height = 0; ::odGetWndSize(m_pOnscreen, width, height); ::glCopyPixels(0, 0, width, height, GL_COLOR); } #endif } if (m_externalFBO) ::glFinish(); } bool isExtensionBasedEmulation() const { return true; } bool isExtensionSupported(const char *pExtensionName) { return GLES2_ParseExtension(pExtensionName, NULL); } void *acquireExtensionFunctionPtr(const char *pFunctionName) { return GLES2_GetProcAddress(pFunctionName); } }; OdSmartPtr OdTrGL2LocalContext::createLocalContext(OdTrVisRenderClient *pDevice) { OdTrGL2LocalContextPtr pContext = OdRxObjectImpl::createObject(); pContext->createContext(pDevice); return pContext; } //