diff options
Diffstat (limited to 'src/VBox/HostServices')
78 files changed, 16077 insertions, 3587 deletions
diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.cpp b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp index 8512ca4f..5766a8e9 100644 --- a/src/VBox/HostServices/DragAndDrop/dndmanager.cpp +++ b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp @@ -513,10 +513,13 @@ int DnDHGSendDataMessage::progressCallback(size_t cbDone, void *pvUser) pSelf->m_cbTransfered += cbDone; /* Advance progress info */ - if (pSelf->m_pfnProgressCallback) - return pSelf->m_pfnProgressCallback(100.0 / pSelf->m_cbAll * pSelf->m_cbTransfered, DragAndDropSvc::DND_PROGRESS_RUNNING, pSelf->m_pvProgressUser); - else - return VINF_SUCCESS; + int rc = VINF_SUCCESS; + if ( pSelf->m_pfnProgressCallback + && pSelf->m_cbAll) + rc = pSelf->m_pfnProgressCallback((uint64_t)pSelf->m_cbTransfered * 100 / pSelf->m_cbAll, + DragAndDropSvc::DND_PROGRESS_RUNNING, pSelf->m_pvProgressUser); + + return rc; } /****************************************************************************** @@ -739,7 +742,7 @@ int DnDManager::nextMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paPa clear(); /* Create a new cancel message to inform the guest. */ m_pCurMsg = new DnDHGCancelMessage(); - m_pfnProgressCallback(100.0, DragAndDropSvc::DND_PROGRESS_CANCELLED, m_pvProgressUser); + m_pfnProgressCallback(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, m_pvProgressUser); } DO(("next msg: %d %d %Rrc\n", uMsg, cParms, rc)); diff --git a/src/VBox/HostServices/DragAndDrop/service.cpp b/src/VBox/HostServices/DragAndDrop/service.cpp index 1bb87451..54c50256 100644 --- a/src/VBox/HostServices/DragAndDrop/service.cpp +++ b/src/VBox/HostServices/DragAndDrop/service.cpp @@ -138,15 +138,17 @@ int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient) int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient) { /* Remove all waiters with this clientId. */ - while (!m_clientQueue.isEmpty()) + for (size_t i = 0; i < m_clientQueue.size(); ) { - HGCM::Client *pClient = m_clientQueue.first(); + HGCM::Client *pClient = m_clientQueue.at(i); if (pClient->clientId() == u32ClientID) { m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED); - m_clientQueue.removeFirst(); + m_clientQueue.removeAt(i); delete pClient; } + else + i++; } return VINF_SUCCESS; diff --git a/src/VBox/HostServices/GuestControl/service.cpp b/src/VBox/HostServices/GuestControl/service.cpp index b4ecaf1a..bbfd8cee 100644 --- a/src/VBox/HostServices/GuestControl/service.cpp +++ b/src/VBox/HostServices/GuestControl/service.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2011-2012 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -49,26 +49,34 @@ * wait for getting its PID returned by the client) as well as cancelling blocking * host calls in order the client terminated/crashed (HGCM detects disconnected * clients and reports it to this service's callback). + * + * Starting at VBox 4.2 the context ID itself consists of a session ID, an object + * ID (for example a process or file ID) and a count. This is necessary to not break + * compatibility between older hosts and to manage guest session on the host. */ /******************************************************************************* * Header Files * *******************************************************************************/ -#define LOG_GROUP LOG_GROUP_HGCM +#ifdef LOG_GROUP + #undef LOG_GROUP +#endif +#define LOG_GROUP LOG_GROUP_GUEST_CONTROL #include <VBox/HostServices/GuestControlSvc.h> #include <VBox/log.h> -#include <iprt/asm.h> /* For ASMBreakpoint(). */ #include <iprt/assert.h> #include <iprt/cpp/autores.h> #include <iprt/cpp/utils.h> #include <iprt/err.h> #include <iprt/mem.h> +#include <iprt/list.h> #include <iprt/req.h> #include <iprt/string.h> #include <iprt/thread.h> #include <iprt/time.h> +#include <map> #include <memory> /* for auto_ptr */ #include <string> #include <list> @@ -77,107 +85,836 @@ namespace guestControl { -/** - * Structure for holding all clients with their - * generated host contexts. This is necessary for - * maintaining the relationship between a client and its context IDs. - */ -struct ClientContexts -{ - /** This client ID. */ - uint32_t mClientID; - /** The list of contexts a client is assigned to. */ - std::list< uint32_t > mContextList; - - /** The normal constructor. */ - ClientContexts(uint32_t aClientID) - : mClientID(aClientID) {} -}; -/** The client list + iterator type */ -typedef std::list< ClientContexts > ClientContextsList; -typedef std::list< ClientContexts >::iterator ClientContextsListIter; -typedef std::list< ClientContexts >::const_iterator ClientContextsListIterConst; +/** Flag for indicating that the client only is interested in + * messages of specific context IDs. */ +#define CLIENTSTATE_FLAG_CONTEXTFILTER RT_BIT(0) /** - * Structure for holding an uncompleted guest call. + * Structure for maintaining a pending (that is, a deferred and not yet completed) + * client command. */ -struct ClientWaiter +typedef struct ClientConnection { - /** Client ID; a client can have multiple handles! */ - uint32_t mClientID; /** The call handle */ VBOXHGCMCALLHANDLE mHandle; - /** The call parameters */ - VBOXHGCMSVCPARM *mParms; /** Number of parameters */ uint32_t mNumParms; - + /** The call parameters */ + VBOXHGCMSVCPARM *mParms; /** The standard constructor. */ - ClientWaiter() : mClientID(0), mHandle(0), mParms(NULL), mNumParms(0) {} - /** The normal constructor. */ - ClientWaiter(uint32_t aClientID, VBOXHGCMCALLHANDLE aHandle, - VBOXHGCMSVCPARM aParms[], uint32_t cParms) - : mClientID(aClientID), mHandle(aHandle), mParms(aParms), mNumParms(cParms) {} -}; -/** The guest call list type */ -typedef std::list< ClientWaiter > ClientWaiterList; -typedef std::list< ClientWaiter >::iterator CallListIter; -typedef std::list< ClientWaiter >::const_iterator CallListIterConst; + ClientConnection(void) + : mHandle(0), mNumParms(0), mParms(NULL) {} +} ClientConnection; /** - * Structure for holding a buffered host command. + * Structure for holding a buffered host command which has + * not been processed yet. */ -struct HostCmd +typedef struct HostCommand { + RTLISTNODE Node; + + uint32_t AddRef(void) + { +#ifdef DEBUG_andy + LogFlowFunc(("Adding reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n", + this, mContextID, mRefCount + 1)); +#endif + return ++mRefCount; + } + + uint32_t Release(void) + { +#ifdef DEBUG_andy + LogFlowFunc(("Releasing reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n", + this, mContextID, mRefCount - 1)); +#endif + /* Release reference for current command. */ + Assert(mRefCount); + if (--mRefCount == 0) + Free(); + + return mRefCount; + } + + /** + * Allocates the command with an HGCM request. Needs to be free'd using Free(). + * + * @return IPRT status code. + * @param uMsg Message type. + * @param cParms Number of parameters of HGCM request. + * @param paParms Array of parameters of HGCM request. + */ + int Allocate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) + { + LogFlowFunc(("Allocating pHostCmd=%p, uMsg=%RU32, cParms=%RU32, paParms=%p\n", + this, uMsg, cParms, paParms)); + + if (!cParms) /* At least one parameter (context ID) must be present. */ + return VERR_INVALID_PARAMETER; + + AssertPtrReturn(paParms, VERR_INVALID_POINTER); + + /* Paranoia. */ + if (cParms > 256) + cParms = 256; + + int rc = VINF_SUCCESS; + + /* + * Don't verify anything here (yet), because this function only buffers + * the HGCM data into an internal structure and reaches it back to the guest (client) + * in an unmodified state. + */ + mMsgType = uMsg; + mParmCount = cParms; + if (mParmCount) + { + mpParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount); + if (NULL == mpParms) + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < mParmCount; i++) + { + mpParms[i].type = paParms[i].type; + switch (paParms[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: + mpParms[i].u.uint32 = paParms[i].u.uint32; + break; + + case VBOX_HGCM_SVC_PARM_64BIT: + mpParms[i].u.uint64 = paParms[i].u.uint64; + break; + + case VBOX_HGCM_SVC_PARM_PTR: + mpParms[i].u.pointer.size = paParms[i].u.pointer.size; + if (mpParms[i].u.pointer.size > 0) + { + mpParms[i].u.pointer.addr = RTMemAlloc(mpParms[i].u.pointer.size); + if (NULL == mpParms[i].u.pointer.addr) + { + rc = VERR_NO_MEMORY; + break; + } + else + memcpy(mpParms[i].u.pointer.addr, + paParms[i].u.pointer.addr, + mpParms[i].u.pointer.size); + } + else + { + /* Size is 0 -- make sure we don't have any pointer. */ + mpParms[i].u.pointer.addr = NULL; + } + break; + + default: + break; + } + if (RT_FAILURE(rc)) + break; + } + } + + if (RT_SUCCESS(rc)) + { + /* + * Assume that the context ID *always* is the first parameter, + * assign the context ID to the command. + */ + rc = mpParms[0].getUInt32(&mContextID); + + /* Set timestamp so that clients can distinguish between already + * processed commands and new ones. */ + mTimestamp = RTTimeNanoTS(); + } + + LogFlowFunc(("Returned with rc=%Rrc\n", rc)); + return rc; + } + + /** + * Frees the buffered HGCM request. + * + * @return IPRT status code. + */ + void Free(void) + { + AssertMsg(mRefCount == 0, ("pHostCmd=%p, CID=%RU32 still being used by a client (%RU32 refs), cannot free yet\n", + this, mContextID, mRefCount)); + + LogFlowFunc(("Freeing host command pHostCmd=%p, CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n", + this, mContextID, mMsgType, mParmCount, mpParms)); + + for (uint32_t i = 0; i < mParmCount; i++) + { + switch (mpParms[i].type) + { + case VBOX_HGCM_SVC_PARM_PTR: + if (mpParms[i].u.pointer.size > 0) + RTMemFree(mpParms[i].u.pointer.addr); + break; + + default: + break; + } + } + + if (mpParms) + { + RTMemFree(mpParms); + mpParms = NULL; + } + + mParmCount = 0; + + /* Removes the command from its list */ + RTListNodeRemove(&Node); + } + + /** + * Copies data from the buffered HGCM request to the current HGCM request. + * + * @return IPRT status code. + * @param paDstParms Array of parameters of HGCM request to fill the data into. + * @param cPDstarms Number of parameters the HGCM request can handle. + * @param pSrcBuf Parameter buffer to assign. + */ + int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const + { + LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mContextID=%RU32 (Session %RU32)\n", + this, mMsgType, mParmCount, mContextID, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(mContextID))); + + int rc = VINF_SUCCESS; + if (cDstParms != mParmCount) + { + LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n", + cDstParms, mParmCount)); + rc = VERR_INVALID_PARAMETER; + } + + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < mParmCount; i++) + { + if (paDstParms[i].type != mpParms[i].type) + { + LogFlowFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n", + i, paDstParms[i].type, mpParms[i].type)); + rc = VERR_INVALID_PARAMETER; + } + else + { + switch (mpParms[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n", + i, mpParms[i].u.uint32)); +#endif + paDstParms[i].u.uint32 = mpParms[i].u.uint32; + break; + + case VBOX_HGCM_SVC_PARM_64BIT: +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %RU64 (uint64_t)\n", + i, mpParms[i].u.uint64)); +#endif + paDstParms[i].u.uint64 = mpParms[i].u.uint64; + break; + + case VBOX_HGCM_SVC_PARM_PTR: + { +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n", + i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size)); +#endif + if (!mpParms[i].u.pointer.size) + continue; /* Only copy buffer if there actually is something to copy. */ + + if (!paDstParms[i].u.pointer.addr) + rc = VERR_INVALID_PARAMETER; + + if ( RT_SUCCESS(rc) + && paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size) + rc = VERR_BUFFER_OVERFLOW; + + if (RT_SUCCESS(rc)) + { + memcpy(paDstParms[i].u.pointer.addr, + mpParms[i].u.pointer.addr, + mpParms[i].u.pointer.size); + } + + break; + } + + default: + LogFlowFunc(("Parameter %RU32 of type %RU32 is not supported yet\n", + i, mpParms[i].type)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if (RT_FAILURE(rc)) + { + LogFlowFunc(("Parameter %RU32 invalid (%Rrc), refusing\n", + i, rc)); + break; + } + } + } + + LogFlowFunc(("Returned with rc=%Rrc\n", rc)); + return rc; + } + + int Assign(const ClientConnection *pConnection) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + + int rc; + + LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n", + this, mMsgType, mParmCount, mpParms)); + + /* Does the current host command need more parameter space which + * the client does not provide yet? */ + if (mParmCount > pConnection->mNumParms) + { + LogFlowFunc(("pHostCmd=%p requires %RU32 parms, only got %RU32 from client\n", + this, mParmCount, pConnection->mNumParms)); + + /* + * So this call apparently failed because the guest wanted to peek + * how much parameters it has to supply in order to successfully retrieve + * this command. Let's tell him so! + */ + rc = VERR_TOO_MUCH_DATA; + } + else + { + rc = CopyTo(pConnection->mParms, pConnection->mNumParms); + + /* + * Has there been enough parameter space but the wrong parameter types + * were submitted -- maybe the client was just asking for the next upcoming + * host message? + * + * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA + * in every case. + */ + if (RT_FAILURE(rc)) + rc = VERR_TOO_MUCH_DATA; + } + + return rc; + } + + int Peek(const ClientConnection *pConnection) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + + LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n", + this, mMsgType, mParmCount, mpParms)); + + if (pConnection->mNumParms >= 2) + { + pConnection->mParms[0].setUInt32(mMsgType); /* Message ID */ + pConnection->mParms[1].setUInt32(mParmCount); /* Required parameters for message */ + } + else + LogFlowFunc(("Warning: Client has not (yet) submitted enough parameters (%RU32, must be at least 2) to at least peak for the next message\n", + pConnection->mNumParms)); + + /* + * Always return VERR_TOO_MUCH_DATA data here to + * keep it compatible with older clients and to + * have correct accounting (mHostRc + mHostCmdTries). + */ + return VERR_TOO_MUCH_DATA; + } + + /** Reference count for keeping track how many connected + * clients still need to process this command until it can + * be removed. */ + uint32_t mRefCount; /** The context ID this command belongs to. Will be extracted - * from the HGCM parameters. */ + * *always* from HGCM parameter [0]. */ uint32_t mContextID; - /** How many times the host service has tried to deliver this - * command to the guest. */ - uint32_t mTries; /** Dynamic structure for holding the HGCM parms */ - VBOXGUESTCTRPARAMBUFFER mParmBuf; + uint32_t mMsgType; + /** Number of HGCM parameters. */ + uint32_t mParmCount; + /** Array of HGCM parameters. */ + PVBOXHGCMSVCPARM mpParms; + /** Incoming timestamp (us). */ + uint64_t mTimestamp; +} HostCommand; +typedef std::list< HostCommand *> HostCmdList; +typedef std::list< HostCommand *>::iterator HostCmdListIter; +typedef std::list< HostCommand *>::const_iterator HostCmdListIterConst; +/** + * Per-client structure used for book keeping/state tracking a + * certain host command. + */ +typedef struct ClientContext +{ + /* Pointer to list node of this command. */ + HostCommand *mpHostCmd; /** The standard constructor. */ - HostCmd() : mContextID(0), mTries(0) {} -}; -/** The host cmd list + iterator type */ -typedef std::list< HostCmd > HostCmdList; -typedef std::list< HostCmd >::iterator HostCmdListIter; -typedef std::list< HostCmd >::const_iterator HostCmdListIterConst; + ClientContext(void) : mpHostCmd(NULL) {} + /** Internal constrcutor. */ + ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {} +} ClientContext; +typedef std::map< uint32_t, ClientContext > ClientContextMap; +typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter; +typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst; + +/** + * Structure for holding a connected guest client + * state. + */ +typedef struct ClientState +{ + ClientState(void) + : mSvcHelpers(NULL), + mID(0), + mFlags(0), + mFilterMask(0), mFilterValue(0), + mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0), + mHostCmdTS(0), + mIsPending(false), + mPeekCount(0) { } + + ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t uClientID) + : mSvcHelpers(pSvcHelpers), + mID(uClientID), + mFlags(0), + mFilterMask(0), mFilterValue(0), + mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0), + mHostCmdTS(0), + mIsPending(false), + mPeekCount(0){ } + + void DequeueAll(void) + { + HostCmdListIter curItem = mHostCmdList.begin(); + while (curItem != mHostCmdList.end()) + curItem = Dequeue(curItem); + } + + void DequeueCurrent(void) + { + HostCmdListIter curCmd = mHostCmdList.begin(); + if (curCmd != mHostCmdList.end()) + Dequeue(curCmd); + } + + HostCmdListIter Dequeue(HostCmdListIter &curItem) + { + HostCommand *pHostCmd = (*curItem); + AssertPtr(pHostCmd); + + if (pHostCmd->Release() == 0) + { + LogFlowFunc(("[Client %RU32] Destroying pHostCmd=%p\n", + mID, (*curItem))); + + delete pHostCmd; + pHostCmd = NULL; + } + + HostCmdListIter nextItem = mHostCmdList.erase(curItem); + + /* Reset everything else. */ + mHostCmdRc = VINF_SUCCESS; + mHostCmdTries = 0; + mPeekCount = 0; + + return nextItem; + } + + int EnqueueCommand(HostCommand *pHostCmd) + { + AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + + try + { + mHostCmdList.push_back(pHostCmd); + pHostCmd->AddRef(); + } + catch (std::bad_alloc) + { + rc = VERR_NO_MEMORY; + } + + return rc; + } + + bool WantsHostCommand(const HostCommand *pHostCmd) const + { + AssertPtrReturn(pHostCmd, false); + +#ifdef DEBUG_andy + LogFlowFunc(("mHostCmdTS=%RU64, pHostCmdTS=%RU64\n", + mHostCmdTS, pHostCmd->mTimestamp)); +#endif + + /* Only process newer commands. */ + if (pHostCmd->mTimestamp <= mHostCmdTS) + return false; + + /* + * If a sesseion filter is set, only obey those commands we're interested in + * by applying our context ID filter mask and compare the result with the + * original context ID. + */ + bool fWant; + if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER) + { + fWant = (pHostCmd->mContextID & mFilterMask) == mFilterValue; + } + else /* Client is interested in all commands. */ + fWant = true; + + LogFlowFunc(("[Client %RU32] mFlags=0x%x, mContextID=%RU32 (session %RU32), mFilterMask=0x%x, mFilterValue=%RU32, fWant=%RTbool\n", + mID, mFlags, pHostCmd->mContextID, + VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->mContextID), + mFilterMask, mFilterValue, fWant)); + + return fWant; + } + + int SetPending(const ClientConnection *pConnection) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + + if (mIsPending) + { + LogFlowFunc(("[Client %RU32] Already is in pending mode\n", mID)); + + /* + * Signal that we don't and can't return yet. + */ + return VINF_HGCM_ASYNC_EXECUTE; + } + + if (mHostCmdList.empty()) + { + AssertMsg(mIsPending == false, + ("Client ID=%RU32 already is pending but tried to receive a new host command\n", mID)); + + mPendingCon.mHandle = pConnection->mHandle; + mPendingCon.mNumParms = pConnection->mNumParms; + mPendingCon.mParms = pConnection->mParms; + + mIsPending = true; + + LogFlowFunc(("[Client %RU32] Is now in pending mode\n", mID)); + + /* + * Signal that we don't and can't return yet. + */ + return VINF_HGCM_ASYNC_EXECUTE; + } + + /* + * Signal that there already is a connection pending. + * Shouldn't happen in daily usage. + */ + AssertMsgFailed(("Client already has a connection pending\n")); + return VERR_SIGNAL_PENDING; + } + + int Run(const ClientConnection *pConnection, + HostCommand *pHostCmd) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + + LogFlowFunc(("[Client %RU32] pConnection=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32, mPeekCount=%RU32\n", + mID, pConnection, mHostCmdRc, mHostCmdTries, mPeekCount)); + + mHostCmdRc = SendReply(pConnection, pHostCmd); + LogFlowFunc(("[Client %RU32] Processing pHostCmd=%p ended with rc=%Rrc\n", + mID, pHostCmd, mHostCmdRc)); + + bool fRemove = false; + if (RT_FAILURE(mHostCmdRc)) + { + mHostCmdTries++; + + /* + * If the client understood the message but supplied too little buffer space + * don't send this message again and drop it after 6 unsuccessful attempts. + * + * Note: Due to legacy reasons this the retry counter has to be even because on + * every peek there will be the actual command retrieval from the client side. + * To not get the actual command if the client actually only wants to peek for + * the next command, there needs to be two rounds per try, e.g. 3 rounds = 6 tries. + * + ** @todo Fix the mess stated above. GUEST_MSG_WAIT should be become GUEST_MSG_PEEK, *only* + * (and every time) returning the next upcoming host command (if any, blocking). Then + * it's up to the client what to do next, either peeking again or getting the actual + * host command via an own GUEST_ type message. + */ + if (mHostCmdRc == VERR_TOO_MUCH_DATA) + { + if (mHostCmdTries == 6) + fRemove = true; + } + /* Client did not understand the message or something else weird happened. Try again one + * more time and drop it if it didn't get handled then. */ + else if (mHostCmdTries > 1) + fRemove = true; + } + else + fRemove = true; /* Everything went fine, remove it. */ + + LogFlowFunc(("[Client %RU32] Tried pHostCmd=%p for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n", + mID, pHostCmd, mHostCmdTries, mHostCmdRc, fRemove)); + + if (RT_SUCCESS(rc)) + rc = mHostCmdRc; + + if (fRemove) + { + /** @todo Fix this (slow) lookup. Too late today. */ + HostCmdListIter curItem = mHostCmdList.begin(); + while (curItem != mHostCmdList.end()) + { + if ((*curItem) == pHostCmd) + { + Dequeue(curItem); + break; + } + + curItem++; + } + } + + LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", mID, rc)); + return rc; + } + + int RunCurrent(const ClientConnection *pConnection) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + + int rc; + if (mHostCmdList.empty()) + { + rc = SetPending(pConnection); + } + else + { + AssertMsgReturn(!mIsPending, + ("Client ID=%RU32 still is in pending mode; can't use another connection\n", mID), VERR_INVALID_PARAMETER); + + HostCmdListIter curCmd = mHostCmdList.begin(); + Assert(curCmd != mHostCmdList.end()); + HostCommand *pHostCmd = (*curCmd); + AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER); + + rc = Run(pConnection, pHostCmd); + } + + return rc; + } + + int Wakeup(void) + { + int rc = VINF_NO_CHANGE; + + if (mIsPending) + { + LogFlowFunc(("[Client %RU32] Waking up ...\n", mID)); + + rc = VINF_SUCCESS; + + HostCmdListIter curCmd = mHostCmdList.begin(); + if (curCmd != mHostCmdList.end()) + { + HostCommand *pHostCmd = (*curCmd); + AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER); + + LogFlowFunc(("[Client %RU32] Current host command is pHostCmd=%p, CID=%RU32, cmdType=%RU32, cmdParms=%RU32, refCount=%RU32\n", + mID, pHostCmd, pHostCmd->mContextID, pHostCmd->mMsgType, pHostCmd->mParmCount, pHostCmd->mRefCount)); + + rc = Run(&mPendingCon, pHostCmd); + } + else + AssertMsgFailed(("Waking up client ID=%RU32 with no host command in queue is a bad idea\n", mID)); + + return rc; + } + + return VINF_NO_CHANGE; + } + + int CancelWaiting(int rcPending) + { + LogFlowFunc(("[Client %RU32] Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n", + mID, rcPending, mIsPending, mPendingCon.mNumParms, mFlags)); + + int rc; + if ( mIsPending + && mPendingCon.mNumParms >= 2) + { + mPendingCon.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */ + mPendingCon.mParms[1].setUInt32(0); /* Required parameters for message. */ + + AssertPtr(mSvcHelpers); + mSvcHelpers->pfnCallComplete(mPendingCon.mHandle, rcPending); + + mIsPending = false; + + rc = VINF_SUCCESS; + } + else if (mPendingCon.mNumParms < 2) + rc = VERR_BUFFER_OVERFLOW; + else /** @todo Enqueue command instead of dropping? */ + rc = VERR_WRONG_ORDER; + + return rc; + } + + int SendReply(const ClientConnection *pConnection, + HostCommand *pHostCmd) + { + AssertPtrReturn(pConnection, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER); + + int rc; + /* If the client is in pending mode, always send back + * the peek result first. */ + if (mIsPending) + { + rc = pHostCmd->Peek(pConnection); + mPeekCount++; + } + else + { + /* If this is the very first peek, make sure to *always* give back the peeking answer + * instead of the actual command, even if this command would fit into the current + * connection buffer. */ + if (!mPeekCount) + { + rc = pHostCmd->Peek(pConnection); + mPeekCount++; + } + else + { + /* Try assigning the host command to the client and store the + * result code for later use. */ + rc = pHostCmd->Assign(pConnection); + if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */ + { + rc = pHostCmd->Peek(pConnection); + mPeekCount++; + } + else + mPeekCount = 0; + } + } + + /* Reset pending status. */ + mIsPending = false; + + /* In any case the client did something, so complete + * the pending call with the result we just got. */ + AssertPtr(mSvcHelpers); + mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc); + + LogFlowFunc(("[Client %RU32] mPeekCount=%RU32, pConnection=%p, pHostCmd=%p, replyRc=%Rrc\n", + mID, mPeekCount, pConnection, pHostCmd, rc)); + return rc; + } + + PVBOXHGCMSVCHELPERS mSvcHelpers; + /** The client's ID. */ + uint32_t mID; + /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */ + uint32_t mFlags; + /** The context ID filter mask, if any. */ + uint32_t mFilterMask; + /** The context ID filter value, if any. */ + uint32_t mFilterValue; + /** Host command list to process. */ + HostCmdList mHostCmdList; + /** Last (most recent) rc after handling the + * host command. */ + int mHostCmdRc; + /** How many times the host service has tried to deliver this + * command to the according client. */ + uint32_t mHostCmdTries; + /** Timestamp (us) of last host command processed. */ + uint64_t mHostCmdTS; + /** + * Flag indicating whether the client currently is pending. + * This means the client waits for a new host command to reply + * and won't return from the waiting call until a new host + * command is available. + */ + bool mIsPending; + /** + * This is necessary for being compatible with older + * Guest Additions. In case there are commands which only + * have two (2) parameters and therefore would fit into the + * GUEST_MSG_WAIT reply immediately, we now can make sure + * that the client first gets back the GUEST_MSG_WAIT results + * first. + */ + uint32_t mPeekCount; + /** The client's pending connection. */ + ClientConnection mPendingCon; +} ClientState; +typedef std::map< uint32_t, ClientState > ClientStateMap; +typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter; +typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst; /** * Class containing the shared information service functionality. */ class Service : public RTCNonCopyable { + private: + /** Type definition for use in callback functions. */ typedef Service SELF; /** HGCM helper functions. */ PVBOXHGCMSVCHELPERS mpHelpers; - /* + /** * Callback function supplied by the host for notification of updates * to properties. */ PFNHGCMSVCEXT mpfnHostCallback; /** User data pointer to be supplied to the host callback function. */ void *mpvHostData; - /** The deferred calls list. */ - ClientWaiterList mClientWaiterList; - /** The host command list. */ - HostCmdList mHostCmds; - /** Client contexts list. */ - ClientContextsList mClientContextsList; - /** Number of connected clients. */ - uint32_t mNumClients; + /** List containing all buffered host commands. */ + RTLISTANCHOR mHostCmdList; + /** Map containing all connected clients. The primary key contains + * the HGCM client ID to identify the client. */ + ClientStateMap mClientStateMap; public: explicit Service(PVBOXHGCMSVCHELPERS pHelpers) : mpHelpers(pHelpers) , mpfnHostCallback(NULL) , mpvHostData(NULL) - , mNumClients(0) { + RTListInit(&mHostCmdList); } /** @@ -204,11 +941,9 @@ public: void *pvClient) { AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); - LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient)); SELF *pSelf = reinterpret_cast<SELF *>(pvService); - int rc = pSelf->clientConnect(u32ClientID, pvClient); - LogFlowFunc (("rc=%Rrc\n", rc)); - return rc; + AssertPtrReturn(pSelf, VERR_INVALID_POINTER); + return pSelf->clientConnect(u32ClientID, pvClient); } /** @@ -220,11 +955,9 @@ public: void *pvClient) { AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); - LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient)); SELF *pSelf = reinterpret_cast<SELF *>(pvService); - int rc = pSelf->clientDisconnect(u32ClientID, pvClient); - LogFlowFunc (("rc=%Rrc\n", rc)); - return rc; + AssertPtrReturn(pSelf, VERR_INVALID_POINTER); + return pSelf->clientDisconnect(u32ClientID, pvClient); } /** @@ -240,10 +973,9 @@ public: VBOXHGCMSVCPARM paParms[]) { AssertLogRelReturnVoid(VALID_PTR(pvService)); - LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms)); SELF *pSelf = reinterpret_cast<SELF *>(pvService); + AssertPtrReturnVoid(pSelf); pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms); - LogFlowFunc (("returning\n")); } /** @@ -256,11 +988,9 @@ public: VBOXHGCMSVCPARM paParms[]) { AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); - LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms)); SELF *pSelf = reinterpret_cast<SELF *>(pvService); - int rc = pSelf->hostCall(u32Function, cParms, paParms); - LogFlowFunc (("rc=%Rrc\n", rc)); - return rc; + AssertPtrReturn(pSelf, VERR_INVALID_POINTER); + return pSelf->hostCall(u32Function, cParms, paParms); } /** @@ -273,217 +1003,31 @@ public: { AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); SELF *pSelf = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pSelf, VERR_INVALID_POINTER); pSelf->mpfnHostCallback = pfnExtension; pSelf->mpvHostData = pvExtension; return VINF_SUCCESS; } + private: - int paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); - void paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf); - int paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, PVBOXGUESTCTRPARAMBUFFER pSrcBuf); + int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]); int clientConnect(uint32_t u32ClientID, void *pvClient); int clientDisconnect(uint32_t u32ClientID, void *pvClient); - int assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); - int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSetMsgFilterSet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSetMsgFilterUnset(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); int cancelHostCmd(uint32_t u32ContextID); - int cancelPendingWaits(uint32_t u32ClientID); - int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); - int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); - void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, - void *pvClient, uint32_t eFunction, uint32_t cParms, - VBOXHGCMSVCPARM paParms[]); + int cancelPendingWaits(uint32_t u32ClientID, int rcPending); + int hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); - int uninit(); + int sessionClose(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int uninit(void); }; - -/** - * Stores a HGCM request in an internal buffer. Needs to be free'd using paramBufferFree(). - * - * @return IPRT status code. - * @param pBuf Buffer to store the HGCM request into. - * @param uMsg Message type. - * @param cParms Number of parameters of HGCM request. - * @param paParms Array of parameters of HGCM request. - */ -int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) -{ - AssertPtrReturn(pBuf, VERR_INVALID_POINTER); - if (cParms) - AssertPtrReturn(paParms, VERR_INVALID_POINTER); - - /* Paranoia. */ - if (cParms > 256) - cParms = 256; - - int rc = VINF_SUCCESS; - - /* - * Don't verify anything here (yet), because this function only buffers - * the HGCM data into an internal structure and reaches it back to the guest (client) - * in an unmodified state. - */ - pBuf->uMsg = uMsg; - pBuf->uParmCount = cParms; - if (pBuf->uParmCount) - { - pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount); - if (NULL == pBuf->pParms) - rc = VERR_NO_MEMORY; - } - - if (RT_SUCCESS(rc)) - { - for (uint32_t i = 0; i < pBuf->uParmCount; i++) - { - pBuf->pParms[i].type = paParms[i].type; - switch (paParms[i].type) - { - case VBOX_HGCM_SVC_PARM_32BIT: - pBuf->pParms[i].u.uint32 = paParms[i].u.uint32; - break; - - case VBOX_HGCM_SVC_PARM_64BIT: - /* Not supported yet. */ - break; - - case VBOX_HGCM_SVC_PARM_PTR: - pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size; - if (pBuf->pParms[i].u.pointer.size > 0) - { - pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size); - if (NULL == pBuf->pParms[i].u.pointer.addr) - { - rc = VERR_NO_MEMORY; - break; - } - else - memcpy(pBuf->pParms[i].u.pointer.addr, - paParms[i].u.pointer.addr, - pBuf->pParms[i].u.pointer.size); - } - else - { - /* Size is 0 -- make sure we don't have any pointer. */ - pBuf->pParms[i].u.pointer.addr = NULL; - } - break; - - default: - break; - } - if (RT_FAILURE(rc)) - break; - } - } - return rc; -} - -/** - * Frees a buffered HGCM request. - * - * @return IPRT status code. - * @param pBuf Parameter buffer to free. - */ -void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf) -{ - AssertPtr(pBuf); - for (uint32_t i = 0; i < pBuf->uParmCount; i++) - { - switch (pBuf->pParms[i].type) - { - case VBOX_HGCM_SVC_PARM_PTR: - if (pBuf->pParms[i].u.pointer.size > 0) - RTMemFree(pBuf->pParms[i].u.pointer.addr); - break; - } - } - if (pBuf->uParmCount) - { - RTMemFree(pBuf->pParms); - pBuf->uParmCount = 0; - } -} - -/** - * Copies data from a buffered HGCM request to the current HGCM request. - * - * @return IPRT status code. - * @param paDstParms Array of parameters of HGCM request to fill the data into. - * @param cPDstarms Number of parameters the HGCM request can handle. - * @param pSrcBuf Parameter buffer to assign. - */ -int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, PVBOXGUESTCTRPARAMBUFFER pSrcBuf) -{ - AssertPtr(pSrcBuf); - int rc = VINF_SUCCESS; - if (cDstParms != pSrcBuf->uParmCount) - { - LogFlowFunc(("Parameter count does not match (got %u, expected %u)\n", - cDstParms, pSrcBuf->uParmCount)); - rc = VERR_INVALID_PARAMETER; - } - else - { - for (uint32_t i = 0; i < pSrcBuf->uParmCount; i++) - { - if (paDstParms[i].type != pSrcBuf->pParms[i].type) - { - LogFlowFunc(("Parameter %u type mismatch (got %u, expected %u)\n", - i, paDstParms[i].type, pSrcBuf->pParms[i].type)); - rc = VERR_INVALID_PARAMETER; - } - else - { - switch (pSrcBuf->pParms[i].type) - { - case VBOX_HGCM_SVC_PARM_32BIT: - paDstParms[i].u.uint32 = pSrcBuf->pParms[i].u.uint32; - break; - - case VBOX_HGCM_SVC_PARM_PTR: - { - if (!pSrcBuf->pParms[i].u.pointer.size) - continue; /* Only copy buffer if there actually is something to copy. */ - - if (!paDstParms[i].u.pointer.addr) - rc = VERR_INVALID_PARAMETER; - - if (paDstParms[i].u.pointer.size < pSrcBuf->pParms[i].u.pointer.size) - rc = VERR_BUFFER_OVERFLOW; - - if (RT_SUCCESS(rc)) - { - memcpy(paDstParms[i].u.pointer.addr, - pSrcBuf->pParms[i].u.pointer.addr, - pSrcBuf->pParms[i].u.pointer.size); - } - - break; - } - - case VBOX_HGCM_SVC_PARM_64BIT: - /* Fall through is intentional. */ - default: - LogFlowFunc(("Parameter %u of type %u is not supported yet\n", - i, pSrcBuf->pParms[i].type)); - rc = VERR_NOT_SUPPORTED; - break; - } - } - - if (RT_FAILURE(rc)) - { - LogFlowFunc(("Parameter %u invalid (rc=%Rrc), refusing\n", - i, rc)); - break; - } - } - } - return rc; -} - /** * Handles a client which just connected. * @@ -493,11 +1037,19 @@ int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, */ int Service::clientConnect(uint32_t u32ClientID, void *pvClient) { - LogFlowFunc(("New client (%ld) connected\n", u32ClientID)); - if (mNumClients < UINT32_MAX) - mNumClients++; - else - AssertMsgFailed(("Max. number of clients reached\n")); + LogFlowFunc(("[Client %RU32] Connected\n", u32ClientID)); +#ifdef VBOX_STRICT + ClientStateMapIterConst it = mClientStateMap.find(u32ClientID); + if (it != mClientStateMap.end()) + { + AssertMsgFailed(("Client with ID=%RU32 already connected when it should not\n", + u32ClientID)); + return VERR_ALREADY_EXISTS; + } +#endif + ClientState clientState(mpHelpers, u32ClientID); + mClientStateMap[u32ClientID] = clientState; + /** @todo Exception handling! */ return VINF_SUCCESS; } @@ -512,141 +1064,63 @@ int Service::clientConnect(uint32_t u32ClientID, void *pvClient) */ int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient) { - LogFlowFunc(("Client (ID=%u, %u clients total) disconnected\n", - u32ClientID, mNumClients)); - Assert(mNumClients > 0); - mNumClients--; - - /* If this was the last connected (guest) client we need to - * unblock all eventually queued up (waiting) host calls. */ - bool fAllClientsDisconnected = mNumClients == 0; - if (fAllClientsDisconnected) - LogFlowFunc(("No connected clients left, notifying all queued up callbacks\n")); + LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n", + u32ClientID, mClientStateMap.size())); + + AssertMsg(mClientStateMap.size(), + ("No clients in list anymore when there should (client ID=%RU32)\n", u32ClientID)); - /* - * Throw out all stale clients. - */ int rc = VINF_SUCCESS; - CallListIter itCall = mClientWaiterList.begin(); - while (itCall != mClientWaiterList.end()) - { - if (itCall->mClientID == u32ClientID) - { - itCall = mClientWaiterList.erase(itCall); - } - else - itCall++; - } + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + AssertMsg(itClientState != mClientStateMap.end(), + ("Clients ID=%RU32 not found in client list when it should be there\n", u32ClientID)); - ClientContextsListIter itContextList = mClientContextsList.begin(); - while ( itContextList != mClientContextsList.end() - && RT_SUCCESS(rc)) + if (itClientState != mClientStateMap.end()) { - /* - * Unblock/call back all queued items of the specified client - * or for all items in case there is no waiting client around - * anymore. - */ - if ( itContextList->mClientID == u32ClientID - || fAllClientsDisconnected) - { - std::list< uint32_t >::iterator itContext = itContextList->mContextList.begin(); - while (itContext != itContextList->mContextList.end()) - { - uint32_t uContextID = (*itContext); - - /* - * Notify the host that clients with u32ClientID are no longer - * around and need to be cleaned up (canceling waits etc). - */ - LogFlowFunc(("Notifying CID=%u of disconnect ...\n", uContextID)); - rc = cancelHostCmd(uContextID); - if (RT_FAILURE(rc)) - { - LogFlowFunc(("Cancelling of CID=%u failed with rc=%Rrc\n", - uContextID, rc)); - /* Keep going. */ - } + itClientState->second.DequeueAll(); - itContext++; - } - itContextList = mClientContextsList.erase(itContextList); - } - else - itContextList++; + mClientStateMap.erase(itClientState); } + bool fAllClientsDisconnected = mClientStateMap.size() == 0; if (fAllClientsDisconnected) { + LogFlowFunc(("All clients disconnected, cancelling all host commands ...\n")); + /* * If all clients disconnected we also need to make sure that all buffered * host commands need to be notified, because Main is waiting a notification * via a (multi stage) progress object. */ - HostCmdListIter itHostCmd; - for (itHostCmd = mHostCmds.begin(); itHostCmd != mHostCmds.end(); itHostCmd++) + HostCommand *pCurCmd = RTListGetFirst(&mHostCmdList, HostCommand, Node); + while (pCurCmd) { - rc = cancelHostCmd(itHostCmd->mContextID); - if (RT_FAILURE(rc)) + HostCommand *pNext = RTListNodeGetNext(&pCurCmd->Node, HostCommand, Node); + bool fLast = RTListNodeIsLast(&mHostCmdList, &pCurCmd->Node); + + int rc2 = cancelHostCmd(pCurCmd->mContextID); + if (RT_FAILURE(rc2)) { - LogFlowFunc(("Cancelling of buffered CID=%u failed with rc=%Rrc\n", - itHostCmd->mContextID, rc)); + LogFlowFunc(("Cancelling host command with CID=%u (refCount=%RU32) failed with rc=%Rrc\n", + pCurCmd->mContextID, pCurCmd->mRefCount, rc2)); /* Keep going. */ } - paramBufferFree(&itHostCmd->mParmBuf); - } - - mHostCmds.clear(); - } - - return rc; -} + while (pCurCmd->Release()) + ; + delete pCurCmd; + pCurCmd = NULL; -/** - * Assigns a specified host command to a client. - * - * @return IPRT status code. - * @param pCmd Host command to send. - * @param callHandle Call handle of the client to send the command to. - * @param cParms Number of parameters. - * @param paParms Array of parameters. - */ -int Service::assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) -{ - AssertPtrReturn(pCmd, VERR_INVALID_POINTER); - int rc; + if (fLast) + break; - /* Does the current host command need more parameter space which - * the client does not provide yet? */ - if (pCmd->mParmBuf.uParmCount > cParms) - { - paParms[0].setUInt32(pCmd->mParmBuf.uMsg); /* Message ID */ - paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */ + pCurCmd = pNext; + } - /* - * So this call apparently failed because the guest wanted to peek - * how much parameters it has to supply in order to successfully retrieve - * this command. Let's tell him so! - */ - rc = VERR_TOO_MUCH_DATA; - } - else - { - rc = paramBufferAssign(paParms, cParms, &pCmd->mParmBuf); - - /* Has there been enough parameter space but the wrong parameter types - * were submitted -- maybe the client was just asking for the next upcoming - * host message? - * - * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA - * in every case. */ - if (RT_FAILURE(rc)) - rc = VERR_TOO_MUCH_DATA; + Assert(RTListIsEmpty(&mHostCmdList)); } - LogFlowFunc(("Returned with rc=%Rrc\n", rc)); return rc; } @@ -660,91 +1134,125 @@ int Service::assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, * @param cParms Number of parameters. * @param paParms Array of parameters. */ -int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, - uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +int Service::clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { - int rc = VINF_SUCCESS; - /* * Lookup client in our list so that we can assign the context ID of * a command to that client. */ - std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin(); - while (it != mClientContextsList.rend()) - { - if (it->mClientID == u32ClientID) - break; - it++; - } + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n", + u32ClientID)); + if (itClientState == mClientStateMap.end()) + return VERR_NOT_FOUND; /* Should never happen. */ - /* Not found? Add client to list. */ - if (it == mClientContextsList.rend()) - { - mClientContextsList.push_back(ClientContexts(u32ClientID)); - it = mClientContextsList.rbegin(); - } - Assert(it != mClientContextsList.rend()); + ClientState &clientState = itClientState->second; + + /* Use the current (inbound) connection. */ + ClientConnection thisCon; + thisCon.mHandle = callHandle; + thisCon.mNumParms = cParms; + thisCon.mParms = paParms; + + return clientState.RunCurrent(&thisCon); +} +int Service::clientSetMsgFilterSet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ /* - * If host command list is empty (nothing to do right now) just - * defer the call until we got something to do (makes the client - * wait, depending on the flags set). + * Lookup client in our list so that we can assign the context ID of + * a command to that client. */ - if (mHostCmds.empty()) /* If command list is empty, defer ... */ - { - mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms)); - rc = VINF_HGCM_ASYNC_EXECUTE; - } - else + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n", + u32ClientID)); + if (itClientState == mClientStateMap.end()) + return VERR_NOT_FOUND; /* Should never happen. */ + + if (cParms != 4) + return VERR_INVALID_PARAMETER; + + uint32_t uValue, uMaskAdd, uMaskRemove; + int rc = paParms[0].getUInt32(&uValue); + if (RT_SUCCESS(rc)) + rc = paParms[1].getUInt32(&uMaskAdd); + if (RT_SUCCESS(rc)) + rc = paParms[2].getUInt32(&uMaskRemove); + /** @todo paParm[3] (flags) not used yet. */ + if (RT_SUCCESS(rc)) { - /* - * Get the next unassigned host command in the list. - */ - HostCmd &curCmd = mHostCmds.front(); - rc = assignHostCmdToGuest(&curCmd, callHandle, cParms, paParms); - if (RT_SUCCESS(rc)) - { - /* Remember which client processes which context (for - * later reference & cleanup). */ - /// @todo r=bird: check if already in the list. - /// @todo Use a map instead of a list? - it->mContextList.push_back(curCmd.mContextID); - - /* Only if the guest really got and understood the message remove it from the list. */ - paramBufferFree(&curCmd.mParmBuf); - mHostCmds.pop_front(); - } - else - { - bool fRemoveCmd = false; - uint32_t uTries = curCmd.mTries++; - - /* If the client understood the message but supplied too little buffer space - * don't send this message again and drop it after 3 unsuccessful attempts. - * The host then should take care of next actions (maybe retry it with a smaller buffer). */ - if ( rc == VERR_BUFFER_OVERFLOW - && uTries >= 3) - { - fRemoveCmd = true; - } - /* Client did not understand the message or something else weird happened. Try again one - * more time and drop it if it didn't get handled then. */ - else if (uTries > 1) - fRemoveCmd = true; - - if (fRemoveCmd) - { - paramBufferFree(&curCmd.mParmBuf); - mHostCmds.pop_front(); - } - } + ClientState &clientState = itClientState->second; + + clientState.mFlags |= CLIENTSTATE_FLAG_CONTEXTFILTER; + if (uMaskAdd) + clientState.mFilterMask |= uMaskAdd; + if (uMaskRemove) + clientState.mFilterMask &= ~uMaskRemove; + + clientState.mFilterValue = uValue; + + LogFlowFunc(("[Client %RU32] Setting message filterMask=0x%x, filterVal=%RU32 set (flags=0x%x, maskAdd=0x%x, maskRemove=0x%x)\n", + u32ClientID, clientState.mFilterMask, clientState.mFilterValue, + clientState.mFlags, uMaskAdd, uMaskRemove)); } + return rc; } +int Service::clientSetMsgFilterUnset(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Lookup client in our list so that we can assign the context ID of + * a command to that client. + */ + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n", + u32ClientID)); + if (itClientState == mClientStateMap.end()) + return VERR_NOT_FOUND; /* Should never happen. */ + + if (cParms != 1) + return VERR_INVALID_PARAMETER; + + ClientState &clientState = itClientState->second; + + clientState.mFlags &= ~CLIENTSTATE_FLAG_CONTEXTFILTER; + clientState.mFilterMask = 0; + clientState.mFilterValue = 0; + + LogFlowFunc(("[Client %RU32} Unset message filter\n", u32ClientID)); + return VINF_SUCCESS; +} + +int Service::clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Lookup client in our list so that we can assign the context ID of + * a command to that client. + */ + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + AssertMsg(itClientState != mClientStateMap.end(), ("Client ID=%RU32 not found when it should be present\n", + u32ClientID)); + if (itClientState == mClientStateMap.end()) + return VERR_NOT_FOUND; /* Should never happen. */ + + if (cParms != 1) + return VERR_INVALID_PARAMETER; + + LogFlowFunc(("[Client %RU32] Skipping current message ...\n", u32ClientID)); + + itClientState->second.DequeueCurrent(); + + return VINF_SUCCESS; +} + /** - * Cancels a buffered host command to unblock waits on Main side - * (via (multi stage) progress objects. + * Cancels a buffered host command to unblock waiting on Main side + * via callbacks. * * @return IPRT status code. * @param u32ContextID Context ID of host command to cancel. @@ -755,14 +1263,11 @@ int Service::cancelHostCmd(uint32_t u32ContextID) LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID)); - CALLBACKDATACLIENTDISCONNECTED data; - data.hdr.u32Magic = CALLBACKDATAMAGIC_CLIENT_DISCONNECTED; - data.hdr.u32ContextID = u32ContextID; - - AssertPtr(mpfnHostCallback); - AssertPtr(mpvHostData); + uint32_t cParms = 0; + VBOXHGCMSVCPARM arParms[2]; + arParms[cParms++].setUInt32(u32ContextID); - return mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data)); + return hostCallback(GUEST_DISCONNECTED, cParms, arParms); } /** @@ -771,28 +1276,15 @@ int Service::cancelHostCmd(uint32_t u32ContextID) * * @return IPRT status code. * @param u32ClientID The client's ID. + * @param rcPending Result code for completing pending operation. */ -int Service::cancelPendingWaits(uint32_t u32ClientID) +int Service::cancelPendingWaits(uint32_t u32ClientID, int rcPending) { - int rc = VINF_SUCCESS; - CallListIter it = mClientWaiterList.begin(); - while (it != mClientWaiterList.end()) - { - if (it->mClientID == u32ClientID) - { - if (it->mNumParms >= 2) - { - it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */ - it->mParms[1].setUInt32(0); /* Required parameters for message. */ - } - if (mpHelpers) - mpHelpers->pfnCallComplete(it->mHandle, rc); - it = mClientWaiterList.erase(it); - } - else - it++; - } - return rc; + ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID); + if (itClientState != mClientStateMap.end()) + return itClientState->second.CancelWaiting(rcPending); + + return VINF_SUCCESS; } /** @@ -804,67 +1296,27 @@ int Service::cancelPendingWaits(uint32_t u32ClientID) * @param cParms Number of parameters. * @param paParms Array of parameters. */ -int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +int Service::hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n", eFunction, cParms, paParms)); - int rc = VINF_SUCCESS; - if ( eFunction == GUEST_EXEC_SEND_STATUS - && cParms == 5) - { - CALLBACKDATAEXECSTATUS data; - data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_STATUS; - paParms[0].getUInt32(&data.hdr.u32ContextID); - - paParms[1].getUInt32(&data.u32PID); - paParms[2].getUInt32(&data.u32Status); - paParms[3].getUInt32(&data.u32Flags); - paParms[4].getPointer(&data.pvData, &data.cbData); - - if (mpfnHostCallback) - rc = mpfnHostCallback(mpvHostData, eFunction, - (void *)(&data), sizeof(data)); - } - else if ( eFunction == GUEST_EXEC_SEND_OUTPUT - && cParms == 5) - { - CALLBACKDATAEXECOUT data; - data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_OUT; - paParms[0].getUInt32(&data.hdr.u32ContextID); - - paParms[1].getUInt32(&data.u32PID); - paParms[2].getUInt32(&data.u32HandleId); - paParms[3].getUInt32(&data.u32Flags); - paParms[4].getPointer(&data.pvData, &data.cbData); - - if (mpfnHostCallback) - rc = mpfnHostCallback(mpvHostData, eFunction, - (void *)(&data), sizeof(data)); - } - else if ( eFunction == GUEST_EXEC_SEND_INPUT_STATUS - && cParms == 5) + + int rc; + if (mpfnHostCallback) { - CALLBACKDATAEXECINSTATUS data; - data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_IN_STATUS; - paParms[0].getUInt32(&data.hdr.u32ContextID); - - paParms[1].getUInt32(&data.u32PID); - paParms[2].getUInt32(&data.u32Status); - paParms[3].getUInt32(&data.u32Flags); - paParms[4].getUInt32(&data.cbProcessed); - - if (mpfnHostCallback) - rc = mpfnHostCallback(mpvHostData, eFunction, - (void *)(&data), sizeof(data)); + VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms); + rc = mpfnHostCallback(mpvHostData, eFunction, + (void *)(&data), sizeof(data)); } else rc = VERR_NOT_SUPPORTED; - LogFlowFunc(("returning %Rrc\n", rc)); + + LogFlowFunc(("Returning rc=%Rrc\n", rc)); return rc; } /** - * Processes a command receiveed from the host side and re-routes it to + * Processes a command received from the host side and re-routes it to * a connect client on the guest. * * @return IPRT status code. @@ -872,7 +1324,7 @@ int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paP * @param cParms Number of parameters. * @param paParms Array of parameters. */ -int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +int Service::hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { /* * If no client is connected at all we don't buffer any host commands @@ -880,68 +1332,65 @@ int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM * waiting for a response from the guest side in case VBoxService on * the guest is not running/system is messed up somehow. */ - if (mNumClients == 0) + if (mClientStateMap.size() == 0) return VERR_NOT_FOUND; - HostCmd newCmd; - int rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms); - if ( RT_SUCCESS(rc) - && cParms) /* Make sure we at least get one parameter (that is, the context ID). */ + + int rc; + + HostCommand *pHostCmd = NULL; + try { - /* - * Assume that the context ID *always* is the first parameter, - * assign the context ID to the command. - */ - newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID); + pHostCmd = new HostCommand(); + rc = pHostCmd->Allocate(eFunction, cParms, paParms); + if (RT_SUCCESS(rc)) + /* rc = */ RTListAppend(&mHostCmdList, &pHostCmd->Node); + } + catch (std::bad_alloc) + { + rc = VERR_NO_MEMORY; } - else if (!cParms) - rc = VERR_INVALID_PARAMETER; if (RT_SUCCESS(rc)) { - LogFlowFunc(("Handling host command CID = %u\n", - newCmd.mContextID)); - - bool fProcessed = false; + LogFlowFunc(("Handling host command CID=%RU32, eFunction=%RU32, cParms=%RU32, paParms=%p, numClients=%zu\n", + pHostCmd->mContextID, eFunction, cParms, paParms, mClientStateMap.size())); - /* Can we wake up a waiting client on guest? */ - if (!mClientWaiterList.empty()) + /* + * Wake up all pending clients which are interested in this + * host command. + */ +#ifdef DEBUG + uint32_t uClientsWokenUp = 0; +#endif + ClientStateMapIter itClientState = mClientStateMap.begin(); + AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n")); + while (itClientState != mClientStateMap.end()) { - ClientWaiter guest = mClientWaiterList.front(); - rc = assignHostCmdToGuest(&newCmd, - guest.mHandle, guest.mNumParms, guest.mParms); + ClientState &clientState = itClientState->second; - /* In any case the client did something, so wake up and remove from list. */ - AssertPtr(mpHelpers); - mpHelpers->pfnCallComplete(guest.mHandle, rc); - mClientWaiterList.pop_front(); - - /* - * If we got back an error (like VERR_TOO_MUCH_DATA or VERR_BUFFER_OVERFLOW) - * we buffer the host command in the next block and return success to the host. - */ - if (RT_FAILURE(rc)) + /* If a client indicates that it it wants the new host command, + * add a reference to not delete it.*/ + if (clientState.WantsHostCommand(pHostCmd)) { - rc = VINF_SUCCESS; + clientState.EnqueueCommand(pHostCmd); + + int rc2 = clientState.Wakeup(); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Waking up client ID=%RU32 failed with rc=%Rrc\n", + itClientState->first, rc2)); +#ifdef DEBUG + uClientsWokenUp++; +#endif } - else /* If command was understood by the client, free and remove from host commands list. */ - { - LogFlowFunc(("Host command CID = %u processed with rc=%Rrc\n", - newCmd.mContextID, rc)); - paramBufferFree(&newCmd.mParmBuf); - } + itClientState++; } - if (!fProcessed) - { - LogFlowFunc(("Buffering host command CID = %u (rc=%Rrc)\n", - newCmd.mContextID, rc)); - - mHostCmds.push_back(newCmd); - } +#ifdef DEBUG + LogFlowFunc(("%RU32 clients have been woken up\n", uClientsWokenUp)); +#endif } - LogFlowFunc(("Returned with rc=%Rrc\n", rc)); return rc; } @@ -959,51 +1408,91 @@ void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, VBOXHGCMSVCPARM paParms[]) { int rc = VINF_SUCCESS; - LogFlowFunc(("u32ClientID = %u, fn = %u, cParms = %u, paParms = 0x%p\n", + LogFlowFunc(("[Client %RU32] eFunction=%RU32, cParms=%RU32, paParms=0x%p\n", u32ClientID, eFunction, cParms, paParms)); try { - switch (eFunction) + /* + * The guest asks the host for the next message to process. + */ + if (eFunction == GUEST_MSG_WAIT) { - /* - * The guest asks the host for the next message to process. - */ - case GUEST_GET_HOST_MSG: - LogFlowFunc(("GUEST_GET_HOST_MSG\n")); - rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms); - break; - - /* - * The guest wants to shut down and asks us (this service) to cancel - * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the - * guest can gracefully shut down. - */ - case GUEST_CANCEL_PENDING_WAITS: - LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n")); - rc = cancelPendingWaits(u32ClientID); - break; - - /* - * For all other regular commands we call our notifyHost - * function. If the current command does not support notifications - * notifyHost will return VERR_NOT_SUPPORTED. - */ - default: - rc = notifyHost(eFunction, cParms, paParms); - break; + LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", u32ClientID)); + rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms); } - if (rc != VINF_HGCM_ASYNC_EXECUTE) + else { - /* Tell the client that the call is complete (unblocks waiting). */ - AssertPtr(mpHelpers); - mpHelpers->pfnCallComplete(callHandle, rc); + switch (eFunction) + { + /* + * A client wants to shut down and asks us (this service) to cancel + * all blocking/pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the + * client can gracefully shut down. + */ + case GUEST_CANCEL_PENDING_WAITS: + LogFlowFunc(("[Client %RU32] GUEST_CANCEL_PENDING_WAITS\n", u32ClientID)); + rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */); + break; + + /* + * The guest only wants certain messages set by the filter mask(s). + * Since VBox 4.3+. + */ + case GUEST_MSG_FILTER_SET: + LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", u32ClientID)); + rc = clientSetMsgFilterSet(u32ClientID, callHandle, cParms, paParms); + break; + + /* + * Unsetting the message filter flag. + */ + case GUEST_MSG_FILTER_UNSET: + LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", u32ClientID)); + rc = clientSetMsgFilterUnset(u32ClientID, callHandle, cParms, paParms); + break; + + /* + * The guest only wants skip the currently assigned messages. Neded + * for dropping its assigned reference of the current assigned host + * command in queue. + * Since VBox 4.3+. + */ + case GUEST_MSG_SKIP: + LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", u32ClientID)); + rc = clientSkipMsg(u32ClientID, callHandle, cParms, paParms); + break; + + /* + * The guest wants to close specific guest session. This is handy for + * shutting down dedicated guest session processes from another process. + */ + case GUEST_SESSION_CLOSE: + LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", u32ClientID)); + rc = sessionClose(u32ClientID, callHandle, cParms, paParms); + break; + + /* + * For all other regular commands we call our hostCallback + * function. If the current command does not support notifications, + * notifyHost will return VERR_NOT_SUPPORTED. + */ + default: + rc = hostCallback(eFunction, cParms, paParms); + break; + } + + if (rc != VINF_HGCM_ASYNC_EXECUTE) + { + /* Tell the client that the call is complete (unblocks waiting). */ + AssertPtr(mpHelpers); + mpHelpers->pfnCallComplete(callHandle, rc); + } } } catch (std::bad_alloc) { rc = VERR_NO_MEMORY; } - LogFlowFunc(("rc = %Rrc\n", rc)); } /** @@ -1014,25 +1503,75 @@ void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { int rc = VERR_NOT_SUPPORTED; - LogFlowFunc(("fn = %u, cParms = %u, paParms = 0x%p\n", + LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n", eFunction, cParms, paParms)); try { - rc = processHostCmd(eFunction, cParms, paParms); + switch (eFunction) + { + /** + * Host + */ + case HOST_CANCEL_PENDING_WAITS: + { + LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n")); + ClientStateMapIter itClientState = mClientStateMap.begin(); + while (itClientState != mClientStateMap.end()) + { + int rc2 = itClientState->second.CancelWaiting(VINF_SUCCESS /* Pending rc. */); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Cancelling waiting for client ID=%RU32 failed with rc=%Rrc", + itClientState->first, rc2)); + itClientState++; + } + rc = VINF_SUCCESS; + break; + } + + default: + rc = hostProcessCommand(eFunction, cParms, paParms); + break; + } } catch (std::bad_alloc) { rc = VERR_NO_MEMORY; } - LogFlowFunc(("rc = %Rrc\n", rc)); return rc; } -int Service::uninit() +/** + * Client asks another client (guest) session to close. + * + * @return IPRT status code. + * @param u32ClientID The client's ID. + * @param callHandle The client's call handle. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int Service::sessionClose(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { - Assert(mHostCmds.empty()); + if (cParms < 2) + return VERR_INVALID_PARAMETER; + uint32_t uContextID, uFlags; + int rc = paParms[0].getUInt32(&uContextID); + if (RT_SUCCESS(rc)) + rc = paParms[1].getUInt32(&uFlags); + + uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID); + + if (RT_SUCCESS(rc)) + rc = hostProcessCommand(HOST_SESSION_CLOSE, cParms, paParms); + + LogFlowFunc(("Closing guest session ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n", + uSessionID, u32ClientID, rc)); + return rc; +} + +int Service::uninit() +{ return VINF_SUCCESS; } @@ -1043,22 +1582,22 @@ using guestControl::Service; /** * @copydoc VBOXHGCMSVCLOAD */ -extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable) +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable) { int rc = VINF_SUCCESS; - LogFlowFunc(("ptable = %p\n", ptable)); + LogFlowFunc(("pTable=%p\n", pTable)); - if (!VALID_PTR(ptable)) + if (!VALID_PTR(pTable)) { rc = VERR_INVALID_PARAMETER; } else { - LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version)); + LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version)); - if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) - || ptable->u32Version != VBOX_HGCM_SVC_VERSION) + if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || pTable->u32Version != VBOX_HGCM_SVC_VERSION) { rc = VERR_VERSION_MISMATCH; } @@ -1067,7 +1606,7 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pta std::auto_ptr<Service> apService; /* No exceptions may propagate outside. */ try { - apService = std::auto_ptr<Service>(new Service(ptable->pHelpers)); + apService = std::auto_ptr<Service>(new Service(pTable->pHelpers)); } catch (int rcThrown) { rc = rcThrown; } catch (...) { @@ -1080,25 +1619,25 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pta * We don't need an additional client data area on the host, * because we're a class which can have members for that :-). */ - ptable->cbClient = 0; + pTable->cbClient = 0; /* Register functions. */ - ptable->pfnUnload = Service::svcUnload; - ptable->pfnConnect = Service::svcConnect; - ptable->pfnDisconnect = Service::svcDisconnect; - ptable->pfnCall = Service::svcCall; - ptable->pfnHostCall = Service::svcHostCall; - ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */ - ptable->pfnLoadState = NULL; /* construction done before restoring suffices */ - ptable->pfnRegisterExtension = Service::svcRegisterExtension; + pTable->pfnUnload = Service::svcUnload; + pTable->pfnConnect = Service::svcConnect; + pTable->pfnDisconnect = Service::svcDisconnect; + pTable->pfnCall = Service::svcCall; + pTable->pfnHostCall = Service::svcHostCall; + pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */ + pTable->pfnLoadState = NULL; /* construction done before restoring suffices */ + pTable->pfnRegisterExtension = Service::svcRegisterExtension; /* Service specific initialization. */ - ptable->pvService = apService.release(); + pTable->pvService = apService.release(); } } } - LogFlowFunc(("returning %Rrc\n", rc)); + LogFlowFunc(("Returning %Rrc\n", rc)); return rc; } diff --git a/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp index 39b940dc..e2b193c3 100644 --- a/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp +++ b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -156,34 +156,40 @@ static int testHost(const VBOXHGCMSVCFNTABLE *pTable) static CMDHOST s_aCmdHostAll[] = { - /** No client connected, invalid command. */ - { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_SUPPORTED }, - { -1 /* Invalid command */, 0, 0, false, VERR_NOT_SUPPORTED }, + #if 0 + /** No client connected. */ + { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_FOUND }, + { -1 /* Invalid command */, 0, 0, false, VERR_NOT_FOUND }, { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND }, { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], false, VERR_NOT_FOUND }, /** No client connected, valid command. */ { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND }, - /** Client connected. */ - { 1024 /* Not existing command */, 0, 0, true, VERR_NOT_SUPPORTED }, - { -1 /* Invalid command */, 0, 0, true, VERR_NOT_SUPPORTED }, + /** Client connected, no parameters given. */ + { HOST_EXEC_SET_INPUT, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, + { 1024 /* Not existing command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, + { -1 /* Invalid command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, /** Client connected, valid parameters given. */ { HOST_CANCEL_PENDING_WAITS, 0, 0, true, VINF_SUCCESS }, { HOST_CANCEL_PENDING_WAITS, 1024, &s_aParms[0], true, VINF_SUCCESS }, { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], true, VINF_SUCCESS}, + #endif /** Client connected, invalid parameters given. */ - { HOST_CANCEL_PENDING_WAITS, 1024, 0, true, VERR_INVALID_POINTER }, - { HOST_CANCEL_PENDING_WAITS, 1, 0, true, VERR_INVALID_POINTER }, - { HOST_CANCEL_PENDING_WAITS, -1, 0, true, VERR_INVALID_POINTER }, + { HOST_EXEC_CMD, 1024, 0, true, VERR_INVALID_POINTER }, + { HOST_EXEC_CMD, 1, 0, true, VERR_INVALID_POINTER }, + { HOST_EXEC_CMD, -1, 0, true, VERR_INVALID_POINTER }, /** Client connected, parameters given. */ { HOST_CANCEL_PENDING_WAITS, 1, &s_aParms[0], true, VINF_SUCCESS }, { HOST_EXEC_CMD, 1, &s_aParms[0], true, VINF_SUCCESS }, { HOST_EXEC_SET_INPUT, 1, &s_aParms[0], true, VINF_SUCCESS }, - { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS } + { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS }, + + /** Client connected, unknown command + valid parameters given. */ + { -1, 1, &s_aParms[0], true, VINF_SUCCESS } }; int rc = testHostCmd(pTable, &s_aCmdHostAll[0], RT_ELEMENTS(s_aCmdHostAll)); @@ -202,57 +208,21 @@ static int testClient(const VBOXHGCMSVCFNTABLE *pTable) /* No commands from host yet. */ static VBOXHGCMSVCPARM s_aParmsGuest[8]; + s_aParmsGuest[0].setUInt32(0 /* Msg type */); + s_aParmsGuest[1].setUInt32(0 /* Parameters */); pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); + GUEST_MSG_WAIT, 2, &s_aParmsGuest[0]); RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); /* Host: Add a dummy command. */ static VBOXHGCMSVCPARM s_aParmsHost[8]; s_aParmsHost[0].setUInt32(1000 /* Context ID */); s_aParmsHost[1].setString("foo.bar"); + s_aParmsHost[2].setString("baz"); - rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 2, &s_aParmsHost[0]); + rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 3, &s_aParmsHost[0]); RTTEST_CHECK_RC_RET(g_hTest, rc, VINF_SUCCESS, rc); - /* Client: Get host command with a invalid parameter count specified. */ - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 1024, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, -1, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, -1, NULL); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 16, NULL); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc); - - /* Client: Get host command with a too small HGCM array. */ - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 0, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc); - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 1, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc); - - /* Client: Get host command without an allocated buffer. */ - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_BUFFER_OVERFLOW, callHandle.rc); - - /* Client: Get host command, this time with a valid buffer. */ - char szBuf[16]; - s_aParmsGuest[1].setPointer(szBuf, sizeof(szBuf)); - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); - - /* Client: Now make sure there's no host message left anymore. */ - pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, - GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]); - RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); - /* Client: Disconnect again. */ int rc2 = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); if (RT_SUCCESS(rc)) diff --git a/src/VBox/HostServices/GuestProperties/service.cpp b/src/VBox/HostServices/GuestProperties/service.cpp index addcbec5..bd3a1626 100644 --- a/src/VBox/HostServices/GuestProperties/service.cpp +++ b/src/VBox/HostServices/GuestProperties/service.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -138,18 +138,20 @@ struct GuestCall VBOXHGCMCALLHANDLE mHandle; /** The function that was requested */ uint32_t mFunction; + /** Number of call parameters. */ + uint32_t mParmsCnt; /** The call parameters */ VBOXHGCMSVCPARM *mParms; /** The default return value, used for passing warnings */ int mRc; /** The standard constructor */ - GuestCall() : u32ClientId(0), mFunction(0) {} + GuestCall(void) : u32ClientId(0), mFunction(0), mParmsCnt(0) {} /** The normal constructor */ GuestCall(uint32_t aClientId, VBOXHGCMCALLHANDLE aHandle, uint32_t aFunction, - VBOXHGCMSVCPARM aParms[], int aRc) - : u32ClientId(aClientId), mHandle(aHandle), mFunction(aFunction), mParms(aParms), - mRc(aRc) {} + uint32_t aParmsCnt, VBOXHGCMSVCPARM aParms[], int aRc) + : u32ClientId(aClientId), mHandle(aHandle), mFunction(aFunction), + mParmsCnt(aParmsCnt), mParms(aParms), mRc(aRc) {} }; /** The guest call list type */ typedef std::list <GuestCall> CallList; @@ -170,7 +172,8 @@ private: RTSTRSPACE mhProperties; /** The number of properties. */ unsigned mcProperties; - /** The list of property changes for guest notifications */ + /** The list of property changes for guest notifications; + * only used for timestamp tracking in notifications at the moment */ PropertyList mGuestNotifications; /** The list of outstanding guest notification calls */ CallList mGuestWaiters; @@ -375,8 +378,8 @@ private: VBOXHGCMSVCPARM paParms[]); int getOldNotificationInternal(const char *pszPattern, uint64_t u64Timestamp, Property *pProp); - int getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop); - void doNotifications(const char *pszProperty, uint64_t u64Timestamp); + int getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property prop); + int doNotifications(const char *pszProperty, uint64_t u64Timestamp); int notifyHost(const char *pszName, const char *pszValue, uint64_t u64Timestamp, const char *pszFlags); @@ -701,35 +704,41 @@ int Service::setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGues } else if (mcProperties < MAX_PROPS) { - /* Create a new string space record. */ - pProp = new Property(pcszName, pcszValue, u64TimeNano, fFlags); - if (pProp) + try { + /* Create a new string space record. */ + pProp = new Property(pcszName, pcszValue, u64TimeNano, fFlags); + AssertPtr(pProp); + if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore)) mcProperties++; else { AssertFailed(); delete pProp; - rc = VERR_INTERNAL_ERROR_3; + + rc = VERR_ALREADY_EXISTS; } } - else + catch (std::bad_alloc) + { rc = VERR_NO_MEMORY; + } } else rc = VERR_TOO_MUCH_DATA; /* - * Send a notification to the host and return. + * Send a notification to the guest and host and return. */ // if (isGuest) /* Notify the host even for properties that the host // * changed. Less efficient, but ensures consistency. */ - doNotifications(pcszName, u64TimeNano); - Log2(("Set string %s, rc=%Rrc, value=%s\n", pcszName, rc, pcszValue)); + int rc2 = doNotifications(pcszName, u64TimeNano); + if (RT_SUCCESS(rc)) + rc = rc2; } - LogFlowThisFunc(("rc = %Rrc (%s = %s)\n", rc, pcszName, pcszValue)); + LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc)); return rc; } @@ -763,7 +772,7 @@ int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGues rc = VERR_INVALID_PARAMETER; if (RT_FAILURE(rc)) { - LogFlowThisFunc(("rc = %Rrc\n", rc)); + LogFlowThisFunc(("rc=%Rrc\n", rc)); return rc; } @@ -787,10 +796,12 @@ int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGues delete pProp; // if (isGuest) /* Notify the host even for properties that the host // * changed. Less efficient, but ensures consistency. */ - doNotifications(pcszName, u64Timestamp); + int rc2 = doNotifications(pcszName, u64Timestamp); + if (RT_SUCCESS(rc)) + rc = rc2; } - LogFlowThisFunc(("rc = %Rrc (%s)\n", rc, pcszName)); + LogFlowThisFunc(("%s: rc=%Rrc\n", pcszName, rc)); return rc; } @@ -967,15 +978,17 @@ int Service::getOldNotificationInternal(const char *pszPatterns, /** Helper query used by getNotification */ -int Service::getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop) +int Service::getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property prop) { - int rc = VINF_SUCCESS; + AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); /* Basic sanity checking. */ + /* Format the data to write to the buffer. */ std::string buffer; uint64_t u64Timestamp; char *pchBuf; uint32_t cbBuf; - rc = paParms[2].getBuffer((void **)&pchBuf, &cbBuf); + + int rc = paParms[2].getBuffer((void **)&pchBuf, &cbBuf); if (RT_SUCCESS(rc)) { char szFlags[MAX_FLAGS_LEN]; @@ -1014,8 +1027,8 @@ int Service::getNotificationWriteOut(VBOXHGCMSVCPARM paParms[], Property prop) * @thread HGCM * @throws can throw std::bad_alloc */ -int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, - VBOXHGCMSVCPARM paParms[]) +int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { int rc = VINF_SUCCESS; char *pszPatterns = NULL; /* shut up gcc */ @@ -1034,9 +1047,9 @@ int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle || RT_FAILURE(paParms[2].getBuffer((void **)&pchBuf, &cbBuf)) /* return buffer */ ) rc = VERR_INVALID_PARAMETER; + if (RT_SUCCESS(rc)) - LogFlow((" pszPatterns=%s, u64Timestamp=%llu\n", pszPatterns, - u64Timestamp)); + LogFlow(("pszPatterns=%s, u64Timestamp=%llu\n", pszPatterns, u64Timestamp)); /* * If no timestamp was supplied or no notification was found in the queue @@ -1045,45 +1058,50 @@ int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle Property prop; if (RT_SUCCESS(rc) && u64Timestamp != 0) rc = getOldNotification(pszPatterns, u64Timestamp, &prop); - if (RT_SUCCESS(rc) && prop.isNull()) + if (RT_SUCCESS(rc)) { - /* - * Check if the client already had the same request. - * Complete the old request with an error in this case. - * Protection against clients, which cancel and resubmits requests. - */ - CallList::iterator it = mGuestWaiters.begin(); - while (it != mGuestWaiters.end()) + if (prop.isNull()) { - const char *pszPatternsExisting; - uint32_t cchPatternsExisting; - int rc3 = it->mParms[0].getString(&pszPatternsExisting, &cchPatternsExisting); - - if ( RT_SUCCESS(rc3) - && u32ClientId == it->u32ClientId - && RTStrCmp(pszPatterns, pszPatternsExisting) == 0) + /* + * Check if the client already had the same request. + * Complete the old request with an error in this case. + * Protection against clients, which cancel and resubmits requests. + */ + CallList::iterator it = mGuestWaiters.begin(); + while (it != mGuestWaiters.end()) { - /* Complete the old request. */ - mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED); - it = mGuestWaiters.erase(it); + const char *pszPatternsExisting; + uint32_t cchPatternsExisting; + int rc3 = it->mParms[0].getString(&pszPatternsExisting, &cchPatternsExisting); + + if ( RT_SUCCESS(rc3) + && u32ClientId == it->u32ClientId + && RTStrCmp(pszPatterns, pszPatternsExisting) == 0) + { + /* Complete the old request. */ + mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED); + it = mGuestWaiters.erase(it); + } + else + ++it; } - else - ++it; - } - mGuestWaiters.push_back(GuestCall(u32ClientId, callHandle, GET_NOTIFICATION, - paParms, rc)); - rc = VINF_HGCM_ASYNC_EXECUTE; - } - /* - * Otherwise reply at once with the enqueued notification we found. - */ - else - { - int rc2 = getNotificationWriteOut(paParms, prop); - if (RT_FAILURE(rc2)) - rc = rc2; + mGuestWaiters.push_back(GuestCall(u32ClientId, callHandle, GET_NOTIFICATION, + cParms, paParms, rc)); + rc = VINF_HGCM_ASYNC_EXECUTE; + } + /* + * Otherwise reply at once with the enqueued notification we found. + */ + else + { + int rc2 = getNotificationWriteOut(cParms, paParms, prop); + if (RT_FAILURE(rc2)) + rc = rc2; + } } + + LogFlowThisFunc(("returning rc=%Rrc\n", rc)); return rc; } @@ -1096,10 +1114,10 @@ int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle * * @thread HGCM service */ -void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp) +int Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp) { - AssertPtrReturnVoid(pszProperty); - LogFlowThisFunc (("pszProperty=%s, u64Timestamp=%llu\n", pszProperty, u64Timestamp)); + AssertPtrReturn(pszProperty, VERR_INVALID_POINTER); + LogFlowThisFunc(("pszProperty=%s, u64Timestamp=%llu\n", pszProperty, u64Timestamp)); /* Ensure that our timestamp is different to the last one. */ if ( !mGuestNotifications.empty() && u64Timestamp == mGuestNotifications.back().mTimestamp) @@ -1121,8 +1139,8 @@ void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp) prop.mFlags = pProp->mFlags; } - /* Release waiters if applicable and add the event to the queue for - * guest notifications */ + /* Release guest waiters if applicable and add the event + * to the queue for guest notifications */ int rc = VINF_SUCCESS; try { @@ -1135,7 +1153,7 @@ void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp) if (prop.Matches(pszPatterns)) { GuestCall curCall = *it; - int rc2 = getNotificationWriteOut(curCall.mParms, prop); + int rc2 = getNotificationWriteOut(curCall.mParmsCnt, curCall.mParms, prop); if (RT_SUCCESS(rc2)) rc2 = curCall.mRc; mpHelpers->pfnCallComplete(curCall.mHandle, rc2); @@ -1144,41 +1162,48 @@ void Service::doNotifications(const char *pszProperty, uint64_t u64Timestamp) else ++it; } + mGuestNotifications.push_back(prop); + + /** @todo r=andy This list does not have a purpose but for tracking + * the timestamps ... */ + if (mGuestNotifications.size() > MAX_GUEST_NOTIFICATIONS) + mGuestNotifications.pop_front(); } catch (std::bad_alloc) { rc = VERR_NO_MEMORY; } - if (mGuestNotifications.size() > MAX_GUEST_NOTIFICATIONS) - mGuestNotifications.pop_front(); - /* - * Host notifications - first case: if the property exists then send its - * current value - */ - if (pProp && mpfnHostCallback != NULL) + if ( RT_SUCCESS(rc) + && mpfnHostCallback) { - char szFlags[MAX_FLAGS_LEN]; - /* Send out a host notification */ - const char *pszValue = prop.mValue.c_str(); - if (RT_SUCCESS(rc)) + /* + * Host notifications - first case: if the property exists then send its + * current value + */ + if (pProp) + { + char szFlags[MAX_FLAGS_LEN]; + /* Send out a host notification */ + const char *pszValue = prop.mValue.c_str(); rc = writeFlags(prop.mFlags, szFlags); - if (RT_SUCCESS(rc)) - rc = notifyHost(pszProperty, pszValue, u64Timestamp, szFlags); - } - - /* - * Host notifications - second case: if the property does not exist then - * send the host an empty value - */ - if (!pProp && mpfnHostCallback != NULL) - { - /* Send out a host notification */ - if (RT_SUCCESS(rc)) + if (RT_SUCCESS(rc)) + rc = notifyHost(pszProperty, pszValue, u64Timestamp, szFlags); + } + /* + * Host notifications - second case: if the property does not exist then + * send the host an empty value + */ + else + { + /* Send out a host notification */ rc = notifyHost(pszProperty, "", u64Timestamp, ""); + } } - LogFlowThisFunc(("returning\n")); + + LogFlowThisFunc(("returning rc=%Rrc\n", rc)); + return rc; } /** @@ -1200,10 +1225,10 @@ int Service::notifyHost(const char *pszName, const char *pszValue, HostCallbackData.pcszValue = pszValue; HostCallbackData.u64Timestamp = u64Timestamp; HostCallbackData.pcszFlags = pszFlags; - int rc = mpfnHostCallback (mpvHostData, 0 /*u32Function*/, - (void *)(&HostCallbackData), - sizeof(HostCallbackData)); - LogFlowFunc (("returning %Rrc\n", rc)); + int rc = mpfnHostCallback(mpvHostData, 0 /*u32Function*/, + (void *)(&HostCallbackData), + sizeof(HostCallbackData)); + LogFlowFunc(("returning rc=%Rrc\n", rc)); return rc; } diff --git a/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp index 782fd87a..a1d3c387 100644 --- a/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp +++ b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -431,7 +431,7 @@ int doSetProperty(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName, */ static void testSetProp(VBOXHGCMSVCFNTABLE *pTable) { - RTTestISub("SET_PROP, SET_PROP_VALUE, SET_PROP_HOST, SET_PROP_VALUE_HOST"); + RTTestISub("SET_PROP, _VALUE, _HOST, _VALUE_HOST"); /** Array of properties for testing SET_PROP_HOST and _GUEST. */ static const struct @@ -745,7 +745,7 @@ struct asyncNotification_ */ static void setupAsyncNotification(VBOXHGCMSVCFNTABLE *pTable) { - RTTestISub("Asynchronous GET_NOTIFICATION call with no notifications are available"); + RTTestISub("Async GET_NOTIFICATION without notifications"); static char s_szPattern[] = ""; g_AsyncNotification.aParms[0].setPointer((void *)s_szPattern, sizeof(s_szPattern)); @@ -796,7 +796,7 @@ static void test2(void) /* Set up the asynchronous notification test */ setupAsyncNotification(&svcTable); testSetProp(&svcTable); - RTTestISub("Checking the data returned by the asynchronous notification call"); + RTTestISub("Async notification call data"); testAsyncNotification(&svcTable); /* Our previous notification call should have completed by now. */ testDelProp(&svcTable); @@ -838,7 +838,7 @@ static int doSetGlobalFlags(VBOXHGCMSVCFNTABLE *pTable, ePropFlags eFlags) */ static void testSetPropROGuest(VBOXHGCMSVCFNTABLE *pTable) { - RTTestISub("SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST calls with READONLYGUEST set globally"); + RTTestISub("global READONLYGUEST and SET_PROP*"); /** Array of properties for testing SET_PROP_HOST and _GUEST with the * READONLYGUEST global flag set. */ @@ -908,7 +908,7 @@ static void testSetPropROGuest(VBOXHGCMSVCFNTABLE *pTable) */ static void testDelPropROGuest(VBOXHGCMSVCFNTABLE *pTable) { - RTTestISub("DEL_PROP and DEL_PROP_HOST calls with RDONLYGUEST set globally"); + RTTestISub("global READONLYGUEST and DEL_PROP*"); /** Array of properties for testing DEL_PROP_HOST and _GUEST with * READONLYGUEST set globally. */ diff --git a/src/VBox/HostServices/HostChannel/HostChannel.cpp b/src/VBox/HostServices/HostChannel/HostChannel.cpp index 989b0796..5b07aa48 100644 --- a/src/VBox/HostServices/HostChannel/HostChannel.cpp +++ b/src/VBox/HostServices/HostChannel/HostChannel.cpp @@ -21,8 +21,10 @@ #include "HostChannel.h" + static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance, uint32_t u32Id, const void *pvEvent, uint32_t cbEvent); +static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel); /* A registered provider of channels. */ @@ -62,12 +64,23 @@ struct VBOXHOSTCHCTX RTLISTANCHOR listProviders; }; +/* The channel callbacks context. The provider passes the pointer as a callback parameter. + * Created for the provider and deleted when the provider says so. + */ +typedef struct VBOXHOSTCHCALLBACKCTX +{ + RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */ + + VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */ +} VBOXHOSTCHCALLBACKCTX; + /* Only one service instance is supported. */ static VBOXHOSTCHCTX g_ctx = { false }; -static VBOXHOSTCHANNELCALLBACKS g_callbacks = +static VBOXHOSTCHANNELCALLBACKS g_callbacks = { - HostChannelCallbackEvent + HostChannelCallbackEvent, + HostChannelCallbackDeleted }; @@ -215,17 +228,19 @@ static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle) static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance) { - /* @todo free u32Handle? */ + HOSTCHLOG(("HostChannel: destroy %p\n", pInstance)); } static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance) { + HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs)); return ASMAtomicIncS32(&pInstance->cRefs); } static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance) { int32_t c = ASMAtomicDecS32(&pInstance->cRefs); + HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs)); Assert(c >= 0); if (c == 0) { @@ -250,8 +265,13 @@ static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppI if (RT_SUCCESS(rc)) { + /* Used by the client, that is in the list of channels. */ vhcInstanceAddRef(pInstance); + /* Add to the list of created channel instances. It is inactive while pClient is 0. */ + RTListAppend(&pClient->listChannels, &pInstance->nodeClient); + /* Return to the caller. */ + vhcInstanceAddRef(pInstance); *ppInstance = pInstance; } @@ -282,7 +302,8 @@ static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u VBOXHOSTCHINSTANCE *pIter; RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient) { - if (pIter->u32Handle == u32Handle) + if ( pIter->pClient + && pIter->u32Handle == u32Handle) { pInstance = pIter; @@ -302,6 +323,11 @@ static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient { VBOXHOSTCHINSTANCE *pInstance = NULL; + if (pvChannel == NULL) + { + return NULL; + } + int rc = vboxHostChannelLock(); if (RT_SUCCESS(rc)) @@ -309,7 +335,8 @@ static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient VBOXHOSTCHINSTANCE *pIter; RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient) { - if (pIter->pvChannel == pvChannel) + if ( pIter->pClient + && pIter->pvChannel == pvChannel) { pInstance = pIter; @@ -327,18 +354,118 @@ static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance) { + HOSTCHLOG(("HostChannel: detach %p\n", pInstance)); + if (pInstance->pProvider) { pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel); RTListNodeRemove(&pInstance->nodeProvider); vhcProviderRelease(pInstance->pProvider); - vhcInstanceRelease(pInstance); /* Not in the list anymore. */ + pInstance->pProvider = NULL; + vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */ + } + + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + RTListNodeRemove(&pInstance->nodeClient); + + vboxHostChannelUnlock(); + + vhcInstanceRelease(pInstance); /* Not used by the client anymore. */ + } +} + +/* + * Channel callback contexts. + */ +static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx) +{ + int rc = VINF_SUCCESS; + + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX)); + + if (pCallbackCtx != NULL) + { + /* The callback context is accessed by the providers threads. */ + rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient); + pCallbackCtx->pClient = pClient; + + vboxHostChannelUnlock(); + } + else + { + RTMemFree(pCallbackCtx); + } + } + else + { + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + *ppCallbackCtx = pCallbackCtx; } - RTListNodeRemove(&pInstance->nodeClient); - vhcInstanceRelease(pInstance); /* Not in the list anymore. */ + return rc; } +static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx) +{ + int rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient; + + if (pClient != NULL) + { + /* The callback is associated with a client. + * Check that the callback is in the list and remove it from the list. + */ + bool fFound = false; + + VBOXHOSTCHCALLBACKCTX *pIter; + RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + if (pIter == pCallbackCtx) + { + fFound = true; + break; + } + } + + if (fFound) + { + RTListNodeRemove(&pCallbackCtx->nodeClient); + } + else + { + AssertFailed(); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + /* It is not in the clients anymore. May be the client has been disconnected. + * Just free the memory. + */ + } + + vboxHostChannelUnlock(); + } + + if (RT_SUCCESS(rc)) + { + RTMemFree(pCallbackCtx); + } + + return rc; +} /* * Host channel service functions. @@ -382,12 +509,28 @@ int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient) RTListInit(&pClient->listChannels); RTListInit(&pClient->listEvents); + RTListInit(&pClient->listContexts); return VINF_SUCCESS; } void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient) { + /* Clear the list of contexts and prevent acceess to the client. */ + int rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHCALLBACKCTX *pIter; + VBOXHOSTCHCALLBACKCTX *pNext; + RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + pIter->pClient = NULL; + RTListNodeRemove(&pIter->nodeClient); + } + + vboxHostChannelUnlock(); + } + /* If there are attached channels, detach them. */ VBOXHOSTCHINSTANCE *pIter; VBOXHOSTCHINSTANCE *pIterNext; @@ -417,28 +560,44 @@ int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient, if (RT_SUCCESS(rc)) { - void *pvChannel = NULL; - rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider, - &pvChannel, - u32Flags, - &g_callbacks, pClient); + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL; + rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx); + if (RT_SUCCESS(rc)) { - vhcProviderAddRef(pProvider); - pInstance->pProvider = pProvider; + void *pvChannel = NULL; + rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider, + &pvChannel, + u32Flags, + &g_callbacks, pCallbackCtx); - pInstance->pClient = pClient; - pInstance->pvChannel = pvChannel; + if (RT_SUCCESS(rc)) + { + vhcProviderAddRef(pProvider); + pInstance->pProvider = pProvider; - vhcInstanceAddRef(pInstance); /* Referenced by the list client's channels. */ - RTListAppend(&pClient->listChannels, &pInstance->nodeClient); + pInstance->pClient = pClient; + pInstance->pvChannel = pvChannel; + + /* It is already in the channels list of the client. */ + + vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */ + RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider); - vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */ - RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider); + *pu32Handle = pInstance->u32Handle; - *pu32Handle = pInstance->u32Handle; + HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle)); + } - HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle)); + if (RT_FAILURE(rc)) + { + vhcCallbackCtxDelete(pCallbackCtx); + } + } + + if (RT_FAILURE(rc)) + { + vhcInstanceDetach(pInstance); } vhcInstanceRelease(pInstance); @@ -488,10 +647,12 @@ int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); - if ( pInstance - && pInstance->pProvider) + if (pInstance) { - pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData); + if (pInstance->pProvider) + { + pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData); + } vhcInstanceRelease(pInstance); } @@ -516,14 +677,16 @@ int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); - if ( pInstance - && pInstance->pProvider) + if (pInstance) { - rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData, - pu32SizeReceived, pu32SizeRemaining); + if (pInstance->pProvider) + { + rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData, + pu32SizeReceived, pu32SizeRemaining); - HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n", - pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining)); + HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n", + pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining)); + } vhcInstanceRelease(pInstance); } @@ -550,12 +713,14 @@ int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); - if ( pInstance - && pInstance->pProvider) + if (pInstance) { - pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code, - pvParm, cbParm, - pvData, cbData, pu32SizeDataReturned); + if (pInstance->pProvider) + { + pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code, + pvParm, cbParm, + pvData, cbData, pu32SizeDataReturned); + } vhcInstanceRelease(pInstance); } @@ -578,17 +743,25 @@ typedef struct VBOXHOSTCHANNELEVENT uint32_t cbEvent; } VBOXHOSTCHANNELEVENT; -/* This is called under the lock. */ -int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient, - bool *pfEvent, - uint32_t *pu32Handle, - uint32_t *pu32Id, - void *pvParm, - uint32_t cbParm, - uint32_t *pcbParmOut) +int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient, + bool *pfEvent, + VBOXHGCMCALLHANDLE callHandle, + VBOXHGCMSVCPARM *paParms) { - /* Check if there is something in the client's event queue. */ + int rc = vboxHostChannelLock(); + if (RT_FAILURE(rc)) + { + return rc; + } + if (pClient->fAsync) + { + /* If there is a wait request already, cancel it. */ + vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); + pClient->fAsync = false; + } + + /* Check if there is something in the client's event queue. */ VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent); HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent)); @@ -598,61 +771,119 @@ int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient, /* Report the event. */ RTListNodeRemove(&pEvent->NodeEvent); - *pfEvent = true; - *pu32Handle = pEvent->u32ChannelHandle; - *pu32Id = pEvent->u32Id; - - uint32_t cbToCopy = RT_MIN(cbParm, pEvent->cbEvent); - - HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbParm %d, cbEvent %d\n", - pClient->u32ClientID, cbParm, pEvent->cbEvent)); + HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n", + pClient->u32ClientID, pEvent->cbEvent)); - if (cbToCopy > 0) - { - memcpy(pvParm, pEvent->pvEvent, cbToCopy); - } + vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle, + pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent); - *pcbParmOut = cbToCopy; + *pfEvent = true; RTMemFree(pEvent); } else { + /* No event available at the time. Process asynchronously. */ + pClient->fAsync = true; + pClient->async.callHandle = callHandle; + pClient->async.paParms = paParms; + /* Tell the caller that there is no event. */ *pfEvent = false; } - return VINF_SUCCESS; + vboxHostChannelUnlock(); + return rc; +} + +int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient) +{ + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + if (pClient->fAsync) + { + /* If there is a wait request alredy, cancel it. */ + vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); + + pClient->fAsync = false; + } + + vboxHostChannelUnlock(); + } + + return rc; } +/* @thread provider */ static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel, uint32_t u32Id, const void *pvEvent, uint32_t cbEvent) { - VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvCallbacks; + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks; - VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel); + int rc = vboxHostChannelLock(); + if (RT_FAILURE(rc)) + { + return; + } - HOSTCHLOG(("HostChannel: CallbackEvent: (%d) instance %p\n", - pClient->u32ClientID, pInstance)); + /* Check that the structure is still associated with a client. + * The client can disconnect and will be invalid. + */ + VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient; - if (!pInstance) + if (pClient == NULL) + { + vboxHostChannelUnlock(); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n")); + + /* The client does not exist anymore, skip the event. */ + return; + } + + bool fFound = false; + + VBOXHOSTCHCALLBACKCTX *pIter; + RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + if (pIter == pCallbackCtx) + { + fFound = true; + break; + } + } + + if (!fFound) { -#ifdef DEBUG_sunlover AssertFailed(); -#endif + + vboxHostChannelUnlock(); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n")); + + /* The context is not in the list of contexts. Skip the event. */ return; } - int rc = vboxHostChannelLock(); - if (RT_FAILURE(rc)) + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n", + pCallbackCtx, pClient->u32ClientID, pInstance)); + + if (!pInstance) { + /* Instance was already detached. Skip the event. */ + vboxHostChannelUnlock(); + return; } uint32_t u32ChannelHandle = pInstance->u32Handle; HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n", - pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent)); + pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent)); /* Check whether the event is waited. */ if (pClient->fAsync) @@ -684,18 +915,19 @@ static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvCh pEvent->cbEvent = cbEvent; - if (RT_SUCCESS(rc)) - { - RTListAppend(&pClient->listEvents, &pEvent->NodeEvent); - } - else - { - RTMemFree(pEvent); - } + RTListAppend(&pClient->listEvents, &pEvent->NodeEvent); } } vboxHostChannelUnlock(); + + vhcInstanceRelease(pInstance); +} + +/* @thread provider */ +static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel) +{ + vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks); } int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient, diff --git a/src/VBox/HostServices/HostChannel/HostChannel.h b/src/VBox/HostServices/HostChannel/HostChannel.h index 173faf2c..190f4b98 100644 --- a/src/VBox/HostServices/HostChannel/HostChannel.h +++ b/src/VBox/HostServices/HostChannel/HostChannel.h @@ -45,6 +45,8 @@ typedef struct VBOXHOSTCHCLIENT RTLISTANCHOR listChannels; uint32_t volatile u32HandleSrc; + RTLISTANCHOR listContexts; /* Callback contexts. */ + RTLISTANCHOR listEvents; bool fAsync; /* Guest is waiting for a message. */ @@ -95,13 +97,12 @@ int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient, uint32_t cbData, uint32_t *pu32SizeDataReturned); -int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient, - bool *pfEvent, - uint32_t *pu32Handle, - uint32_t *pu32Id, - void *pvParm, - uint32_t cbParm, - uint32_t *pcbParmOut); +int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient, + bool *pfEvent, + VBOXHGCMCALLHANDLE callHandle, + VBOXHGCMSVCPARM *paParms); + +int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient); int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient, const char *pszName, @@ -118,7 +119,16 @@ int vboxHostChannelRegister(const char *pszName, int vboxHostChannelUnregister(const char *pszName); -void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, uint32_t u32ChannelHandle, - uint32_t u32Id, const void *pvEvent, uint32_t cbEvent); +void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent); + +void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent); #endif /* __VBOXHOSTCHANNEL__H */ diff --git a/src/VBox/HostServices/HostChannel/service.cpp b/src/VBox/HostServices/HostChannel/service.cpp index c632d9d0..a2042985 100644 --- a/src/VBox/HostServices/HostChannel/service.cpp +++ b/src/VBox/HostServices/HostChannel/service.cpp @@ -106,30 +106,46 @@ void vboxHostChannelUnlock(void) RTCritSectLeave(&g_critsect); } -/* This is called under the lock. */ -void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, - uint32_t u32ChannelHandle, - uint32_t u32Id, - const void *pvEvent, - uint32_t cbEvent) +void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent) { if (cbEvent > 0) { void *pvParm = NULL; uint32_t cbParm = 0; - VBoxHGCMParmPtrGet(&pClient->async.paParms[2], &pvParm, &cbParm); + VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm); uint32_t cbToCopy = RT_MIN(cbParm, cbEvent); if (cbToCopy > 0) { + Assert(pvParm); memcpy(pvParm, pvEvent, cbToCopy); } } - VBoxHGCMParmUInt32Set(&pClient->async.paParms[0], u32ChannelHandle); - VBoxHGCMParmUInt32Set(&pClient->async.paParms[1], u32Id); - VBoxHGCMParmUInt32Set(&pClient->async.paParms[3], cbEvent); + VBoxHGCMParmUInt32Set(&paParms[0], u32ChannelHandle); + VBoxHGCMParmUInt32Set(&paParms[1], u32Id); + VBoxHGCMParmUInt32Set(&paParms[3], cbEvent); +} + +/* This is called under the lock. */ +void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent) +{ + Assert(RTCritSectIsOwner(&g_critsect)); + + vboxHostChannelEventParmsSet(pClient->async.paParms, + u32ChannelHandle, + u32Id, + pvEvent, + cbEvent); LogRelFlow(("svcCall: CallComplete for pending\n")); @@ -437,56 +453,18 @@ static DECLCALLBACK(void) svcCall(void *pvService, } else { - void *pvParm; - uint32_t cbParm; + bool fEvent = false; - rc = VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm); + rc = vboxHostChannelEventWait(pClient, &fEvent, callHandle, paParms); if (RT_SUCCESS(rc)) { - /* This is accessed from the SVC thread and other threads. */ - rc = vboxHostChannelLock(); - - if (RT_SUCCESS(rc)) + if (!fEvent) { - if (pClient->fAsync) - { - /* If there is a wait request already, cancel it. */ - vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); - - pClient->fAsync = false; - } - - bool fEvent = false; - uint32_t u32Handle = 0; - uint32_t u32Id = 0; - uint32_t cbParmOut = 0; - - rc = vboxHostChannelQueryEvent(pClient, &fEvent, &u32Handle, &u32Id, - pvParm, cbParm, &cbParmOut); - - if (RT_SUCCESS(rc)) - { - if (fEvent) - { - VBoxHGCMParmUInt32Set(&paParms[0], u32Handle); - VBoxHGCMParmUInt32Set(&paParms[1], u32Id); - VBoxHGCMParmUInt32Set(&paParms[3], cbParmOut); - } - else - { - /* No event available at the time. Process asynchronously. */ - fAsynchronousProcessing = true; - - pClient->fAsync = true; - pClient->async.callHandle = callHandle; - pClient->async.paParms = paParms; + /* No event available at the time. Process asynchronously. */ + fAsynchronousProcessing = true; - LogRel2(("svcCall: async.\n")); - } - } - - vboxHostChannelUnlock(); + LogRel2(("svcCall: async.\n")); } } } @@ -502,21 +480,7 @@ static DECLCALLBACK(void) svcCall(void *pvService, } else { - /* This is accessed from the SVC thread and other threads. */ - rc = vboxHostChannelLock(); - - if (RT_SUCCESS(rc)) - { - if (pClient->fAsync) - { - /* If there is a wait request alredy, cancel it. */ - vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); - - pClient->fAsync = false; - } - - vboxHostChannelUnlock(); - } + rc = vboxHostChannelEventCancel(pClient); } } break; diff --git a/src/VBox/HostServices/Makefile.kmk b/src/VBox/HostServices/Makefile.kmk index 4320d434..acb26fdd 100644 --- a/src/VBox/HostServices/Makefile.kmk +++ b/src/VBox/HostServices/Makefile.kmk @@ -42,7 +42,9 @@ endif ifdef VBOX_WITH_DRAG_AND_DROP include $(PATH_SUB_CURRENT)/DragAndDrop/Makefile.kmk endif -include $(PATH_SUB_CURRENT)/HostChannel/Makefile.kmk +ifdef VBOX_WITH_HOST_CHANNEL + include $(PATH_SUB_CURRENT)/HostChannel/Makefile.kmk +endif include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp index e10be746..153a1992 100644 --- a/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp +++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h index 81f184be..df38bbed 100644 --- a/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h +++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp index 62045eed..1809cf2d 100644 --- a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp +++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp @@ -173,14 +173,23 @@ int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, ui if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF16PlainText, &outData))) { Log(("Clipboard content is utf-16\n")); - rc = RTUtf16DupEx(&pwszTmp, (PRTUTF16)CFDataGetBytePtr(outData), 0); + + PRTUTF16 pwszString = (PRTUTF16)CFDataGetBytePtr(outData); + if (pwszString) + rc = RTUtf16DupEx(&pwszTmp, pwszString, 0); + else + rc = VERR_INVALID_PARAMETER; } /* Second try is utf-8 */ else if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF8PlainText, &outData))) { Log(("readFromPasteboard: clipboard content is utf-8\n")); - rc = RTStrToUtf16((const char*)CFDataGetBytePtr(outData), &pwszTmp); + const char *pszString = (const char *)CFDataGetBytePtr(outData); + if (pszString) + rc = RTStrToUtf16(pszString, &pwszTmp); + else + rc = VERR_INVALID_PARAMETER; } if (pwszTmp) { diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h index 4e36ff1a..704d778e 100644 --- a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h +++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/darwin.cpp b/src/VBox/HostServices/SharedClipboard/darwin.cpp index 1f7fe0ba..46442a46 100644 --- a/src/VBox/HostServices/SharedClipboard/darwin.cpp +++ b/src/VBox/HostServices/SharedClipboard/darwin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/service.cpp b/src/VBox/HostServices/SharedClipboard/service.cpp index 3070a032..413e772a 100644 --- a/src/VBox/HostServices/SharedClipboard/service.cpp +++ b/src/VBox/HostServices/SharedClipboard/service.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp index b994f627..50bfabb7 100644 --- a/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp +++ b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp @@ -38,7 +38,7 @@ static void testSetMode(void) VBOXHGCMSVCFNTABLE table; uint32_t u32Mode; int rc; - + RTTestISub("Testing HOST_FN_SET_MODE"); rc = setupTable(&table); RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc)); @@ -82,7 +82,7 @@ static void testSetHeadless(void) VBOXHGCMSVCFNTABLE table; bool fHeadless; int rc; - + RTTestISub("Testing HOST_FN_SET_HEADLESS"); rc = setupTable(&table); RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc)); diff --git a/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp index 30633ec8..c3700256 100644 --- a/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp +++ b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedClipboard/x11-stub.cpp b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp index 45d681e0..bb928094 100644 --- a/src/VBox/HostServices/SharedClipboard/x11-stub.cpp +++ b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedFolders/mappings.cpp b/src/VBox/HostServices/SharedFolders/mappings.cpp index 166dd049..30310909 100644 --- a/src/VBox/HostServices/SharedFolders/mappings.cpp +++ b/src/VBox/HostServices/SharedFolders/mappings.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -191,7 +191,7 @@ void testMappingsAdd(RTTEST hTest) * We are always executed from one specific HGCM thread. So thread safe. */ int vbsfMappingsAdd(PSHFLSTRING pFolderName, PSHFLSTRING pMapName, - bool fWritable, bool fAutoMount, bool fSymlinksCreate) + bool fWritable, bool fAutoMount, bool fSymlinksCreate, bool fMissing) { unsigned i; @@ -236,6 +236,7 @@ int vbsfMappingsAdd(PSHFLSTRING pFolderName, PSHFLSTRING pMapName, FolderMapping[i].fWritable = fWritable; FolderMapping[i].fAutoMount = fAutoMount; FolderMapping[i].fSymlinksCreate = fSymlinksCreate; + FolderMapping[i].fMissing = fMissing; /* Check if the host file system is case sensitive */ RTFSPROPERTIES prop; @@ -317,6 +318,8 @@ const char* vbsfMappingsQueryHostRoot(SHFLROOT root) { MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); AssertReturn(pFolderMapping, NULL); + if (pFolderMapping->fMissing) + return NULL; return pFolderMapping->pszFolderName; } @@ -459,7 +462,8 @@ int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWri MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER); - if (pFolderMapping->fValid == true) + if ( pFolderMapping->fValid + && !pFolderMapping->fMissing) *fWritable = pFolderMapping->fWritable; else rc = VERR_FILE_NOT_FOUND; diff --git a/src/VBox/HostServices/SharedFolders/mappings.h b/src/VBox/HostServices/SharedFolders/mappings.h index f920e617..c0a2989f 100644 --- a/src/VBox/HostServices/SharedFolders/mappings.h +++ b/src/VBox/HostServices/SharedFolders/mappings.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,6 +31,8 @@ typedef struct bool fWritable; /**< folder is writable for the guest */ bool fAutoMount; /**< folder will be auto-mounted by the guest */ bool fSymlinksCreate; /**< guest is able to create symlinks */ + bool fMissing; /**< mapping not invalid but host path does not exist. + Any guest operation on such a folder fails! */ } MAPPING; /** Pointer to a MAPPING structure. */ typedef MAPPING *PMAPPING; @@ -40,7 +42,7 @@ void vbsfMappingInit(void); bool vbsfMappingQuery(uint32_t iMapping, PMAPPING *pMapping); int vbsfMappingsAdd(PSHFLSTRING pFolderName, PSHFLSTRING pMapName, - bool fWritable, bool fAutoMount, bool fCreateSymlinks); + bool fWritable, bool fAutoMount, bool fCreateSymlinks, bool fMissing); int vbsfMappingsRemove(PSHFLSTRING pMapName); int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, PSHFLMAPPING pMappings, uint32_t *pcMappings); diff --git a/src/VBox/HostServices/SharedFolders/service.cpp b/src/VBox/HostServices/SharedFolders/service.cpp index bc390aeb..0c724f33 100644 --- a/src/VBox/HostServices/SharedFolders/service.cpp +++ b/src/VBox/HostServices/SharedFolders/service.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -1313,18 +1313,20 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa } else { - LogRel((" Host path '%ls', map name '%ls', %s, automount=%s, create_symlinks=%s\n", + LogRel((" Host path '%ls', map name '%ls', %s, automount=%s, create_symlinks=%s, missing=%s\n", ((SHFLSTRING *)paParms[0].u.pointer.addr)->String.ucs2, ((SHFLSTRING *)paParms[1].u.pointer.addr)->String.ucs2, RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE) ? "writable" : "read-only", RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT) ? "true" : "false", - RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS) ? "true" : "false")); + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS) ? "true" : "false", + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING) ? "true" : "false")); /* Execute the function. */ rc = vbsfMappingsAdd(pFolderName, pMapName, RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE), RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT), - RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS)); + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS), + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING)); if (RT_SUCCESS(rc)) { /* Update parameters.*/ diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.cpp b/src/VBox/HostServices/SharedFolders/shflhandle.cpp index cd67057b..1a5f71bd 100644 --- a/src/VBox/HostServices/SharedFolders/shflhandle.cpp +++ b/src/VBox/HostServices/SharedFolders/shflhandle.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.h b/src/VBox/HostServices/SharedFolders/shflhandle.h index 625c0102..59638b91 100644 --- a/src/VBox/HostServices/SharedFolders/shflhandle.h +++ b/src/VBox/HostServices/SharedFolders/shflhandle.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk index a6f327ef..a2f6120d 100644 --- a/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk +++ b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk @@ -54,12 +54,7 @@ tstShflCase_LIBS = $(LIB_RUNTIME) # HGCM service testcase. # -# As there are differences between the Windows build of the service and others, -# we do an additional build with RT_OS_WINDOWS defined on non-Windows targets. -PROGRAMS += \ - tstSharedFolderService \ - $(if $(eq $(KBUILD_TARGET),win),,tstSharedFolderService-win) - +PROGRAMS += tstSharedFolderService tstSharedFolderService_TEMPLATE = VBOXR3TSTEXE tstSharedFolderService_DEFS = VBOX_WITH_HGCM UNITTEST tstSharedFolderService_INCS = .. @@ -73,6 +68,12 @@ tstSharedFolderService_LDFLAGS.darwin = \ -framework Carbon tstSharedFolderService_LIBS = $(LIB_RUNTIME) +if 0 # Cannot define two RT_OS_XXX macros! +# As there are differences between the Windows build of the service and others, +# we do an additional build with RT_OS_WINDOWS defined on non-Windows targets. +PROGRAMS += \ + tstSharedFolderService \ + $(if $(eq $(KBUILD_TARGET),win),,tstSharedFolderService-win) tstSharedFolderService-win_TEMPLATE = $(tstSharedFolderService_TEMPLATE) tstSharedFolderService-win_DEFS = \ $(tstSharedFolderService_DEFS) \ @@ -82,6 +83,7 @@ tstSharedFolderService-win_SOURCES = $(tstSharedFolderService_SOURCES) tstSharedFolderService-win_LDFLAGS.darwin = \ $(tstSharedFolderService_LDFLAGS.darwin) tstSharedFolderService-win_LIBS = $(tstSharedFolderService_LIBS) +endif endif # VBOX_WITH_TESTCASES diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp index b72e724b..1215090a 100644 --- a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp +++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp @@ -1,6 +1,5 @@ /* $Id: tstSharedFolderService.cpp $ */ /** @file - * * Testcase for the shared folder service vbsf API. * * Note that this is still very threadbare (there is an awful lot which should @@ -11,7 +10,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -39,17 +38,19 @@ #include "teststubs.h" + /****************************************************************************** * Global Variables * ******************************************************************************/ static RTTEST g_hTest = NIL_RTTEST; + /****************************************************************************** * Declarations * ******************************************************************************/ - extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable); + /****************************************************************************** * Helpers * ******************************************************************************/ @@ -102,14 +103,15 @@ static void bufferFromPath(void *pvDest, size_t cb, const char *pcszSrc) } #define ARRAY_FROM_PATH(a, b) \ -do { \ - Assert((a) == (a)); /* Constant parameter */ \ - Assert(sizeof((a)) > 0); \ - bufferFromPath(a, sizeof(a), b); \ -} while(0) + do { \ + Assert((a) == (a)); /* Constant parameter */ \ + Assert(sizeof((a)) > 0); \ + bufferFromPath(a, sizeof(a), b); \ + } while (0) + /****************************************************************************** -* Stub functions * +* Stub functions and data * ******************************************************************************/ static PRTDIR testRTDirClosepDir; @@ -306,7 +308,7 @@ static uint64_t testRTFileSetFMode; extern int testRTFileSetMode(RTFILE File, RTFMODE fMode) { /* RTPrintf("%s: fMode=%llu\n", __PRETTY_FUNCTION__, LLUIFY(fMode)); */ - testRTFileSetFMode = fMode; + testRTFileSetFMode = fMode; return VINF_SUCCESS; } @@ -369,7 +371,7 @@ extern int testRTFileWrite(RTFILE File, const void *pvBuf, size_t cbToWrite, *pcbWritten = strlen(testRTFileWriteData) + 1; return VINF_SUCCESS; } - + extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties) { @@ -383,7 +385,7 @@ extern int testRTFsQueryProperties(const char *pszFsPath, extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial) { RTPrintf("%s\n", __PRETTY_FUNCTION__); return 0; } extern int testRTFsQuerySizes(const char *pszFsPath, PRTFOFF pcbTotal, - RTFOFF *pcbFree, uint32_t *pcbBlock, + RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector) { RTPrintf("%s\n", __PRETTY_FUNCTION__); return 0; } extern int testRTPathQueryInfoEx(const char *pszPath, @@ -398,12 +400,13 @@ extern int testRTPathQueryInfoEx(const char *pszPath, return VINF_SUCCESS; } -extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete) +extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete) { RTPrintf("%s\n", __PRETTY_FUNCTION__); return 0; } extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead) { RTPrintf("%s\n", __PRETTY_FUNCTION__); return 0; } + /****************************************************************************** * Tests * ******************************************************************************/ @@ -484,13 +487,11 @@ struct TESTSHFLSTRING static void fillTestShflString(struct TESTSHFLSTRING *pDest, const char *pcszSource) { - unsigned i; - - AssertRelease( strlen(pcszSource) * 2 + 2 < sizeof(*pDest) - - RT_UOFFSETOF(SHFLSTRING, String)); - pDest->string.u16Size = (uint16_t)strlen(pcszSource) * 2 + 2; - pDest->string.u16Length = (uint16_t)strlen(pcszSource); - for (i = 0; i < strlen(pcszSource) + 1; ++i) + AssertRelease( strlen(pcszSource) * 2 + 2 + < sizeof(*pDest) - RT_UOFFSETOF(SHFLSTRING, String)); + pDest->string.u16Length = (uint16_t)(strlen(pcszSource) * sizeof(RTUTF16)); + pDest->string.u16Size = pDest->string.u16Length + sizeof(RTUTF16); + for (unsigned i = 0; i <= pDest->string.u16Length; ++i) pDest->string.String.ucs2[i] = (uint16_t)pcszSource[i]; } diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp index 96d27cfa..ce94fdb4 100644 --- a/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp +++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedFolders/teststubs.h b/src/VBox/HostServices/SharedFolders/teststubs.h index 3be52fcc..54c9ef4a 100644 --- a/src/VBox/HostServices/SharedFolders/teststubs.h +++ b/src/VBox/HostServices/SharedFolders/teststubs.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedFolders/vbsf.cpp b/src/VBox/HostServices/SharedFolders/vbsf.cpp index 617239b8..935e94ce 100644 --- a/src/VBox/HostServices/SharedFolders/vbsf.cpp +++ b/src/VBox/HostServices/SharedFolders/vbsf.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -183,15 +183,24 @@ end: } /** - * Do a simple path check given by pUtf8Path. Verify that the path is within - * the root directory of the mapping. Count '..' and other path components - * and check that we do not go over the root. + * Check the given UTF-8 path for root escapes. + * + * Verify that the path is within the root directory of the mapping. Count '..' + * and other path components and check that we do not go over the root. + * + * @returns VBox status code. + * @retval VINF_SUCCESS + * @retval VERR_INVALID_NAME + * + * @param pUtf8Path The path to check. + * @param cchPath The length of the path in chars (not code points, but + * the C type) excluding the string terminator. * * @remarks This function assumes that the path will be appended to the root - * directory of the shared folder mapping. Keep that in mind when checking - * absolute pathes! + * directory of the shared folder mapping. Keep that in mind when + * checking absolute paths! */ -static int vbsfPathCheck(const char *pUtf8Path, size_t cbPath) +static int vbsfPathCheck(const char *pUtf8Path, size_t cchPath) { int rc = VINF_SUCCESS; @@ -202,42 +211,42 @@ static int vbsfPathCheck(const char *pUtf8Path, size_t cbPath) for (;;) { /* Skip leading path delimiters. */ - while ( i < cbPath + while ( i < cchPath && (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')) i++; - if (i >= cbPath) + if (i >= cchPath) break; /* Check if that is a dot component. */ int cDots = 0; - while (i < cbPath && pUtf8Path[i] == '.') + while (i < cchPath && pUtf8Path[i] == '.') { cDots++; i++; } if ( cDots >= 2 /* Consider all multidots sequences as a 'parent dir'. */ - && (i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) + && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) { cParentDirs++; } else if ( cDots == 1 - && (i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) + && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) { /* Single dot, nothing changes. */ } else { /* Skip this component. */ - while ( i < cbPath + while ( i < cchPath && (pUtf8Path[i] != '\\' && pUtf8Path[i] != '/')) i++; cComponents++; } - Assert(i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')); + Assert(i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')); /* Verify counters for every component. */ if (cParentDirs > cComponents) @@ -283,6 +292,7 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING } else { + /** @todo r-bird: Pardon me for asking, but who validates the UTF-8 encoding?*/ memcpy(utf8FullPath, pszRoot, cbRoot); utf8FullPath[cbRoot] = '/'; memcpy(utf8FullPath + cbRoot + 1, &pPath->String.utf8[0], pPath->u16Length); @@ -295,7 +305,7 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING } else { - Log(("vbsfBuildFullPath: RTUtf16ToUtf8 failed with %Rrc\n", rc)); + Log(("vbsfBuildFullPath: vbsfPathCheck failed with %Rrc\n", rc)); } } else @@ -368,6 +378,7 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING if (pPath->u16Length) { /* Convert and copy components. */ + size_t cwcSrc = pPath->u16Length / sizeof(RTUTF16); PRTUTF16 pwszSrc = &pPath->String.ucs2[0]; /* Correct path delimiters */ @@ -384,9 +395,12 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING LogFlow(("Corrected string %ls\n", pwszSrc)); } if (*pwszSrc == RTPATH_DELIMITER) + { pwszSrc++; /* we already appended a delimiter to the first part */ + cwcSrc--; + } - rc = RTUtf16ToUtf8Ex(pwszSrc, RTSTR_MAX, &pszDst, cb, NULL); + rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cb, &cbDst); if (RT_FAILURE(rc)) { AssertFailed(); @@ -396,8 +410,7 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING #endif return rc; } - - cbDst = (uint32_t)strlen(pszDst); + Assert(cbDst == strlen(pszDst)); /* Verify that the path is under the root directory. */ rc = vbsfPathCheck(pszDst, cbDst); @@ -405,7 +418,6 @@ static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING { #ifdef RT_OS_DARWIN RTMemFree(pPath); - pPath = pPathParameter; #endif return rc; } diff --git a/src/VBox/HostServices/SharedFolders/vbsf.h b/src/VBox/HostServices/SharedFolders/vbsf.h index 76979681..0151a223 100644 --- a/src/VBox/HostServices/SharedFolders/vbsf.h +++ b/src/VBox/HostServices/SharedFolders/vbsf.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedOpenGL/Makefile.kmk b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk index 104aa6fd..971c1c9a 100644 --- a/src/VBox/HostServices/SharedOpenGL/Makefile.kmk +++ b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk @@ -131,8 +131,10 @@ VBoxOGLcrserverlib_SOURCES := \ crserverlib/server_getshaders.c \ crserverlib/server_framebuffer.c \ crserverlib/server_glsl.c \ - crserverlib/server_muralfbo.c \ + crserverlib/server_muralfbo.cpp \ crserverlib/server_texture.c \ + crserverlib/server_presenter.cpp \ + crserverlib/server_rpw.cpp \ $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c \ $(VBOX_PATH_CROGL_GENFILES)/server_retval.c \ $(VBOX_PATH_CROGL_GENFILES)/server_get.c \ @@ -149,6 +151,13 @@ endif ifdef VBOX_WITH_CRHGSMI VBoxOGLcrserverlib_DEFS += ifdef VBOX_WITH_CRHGSMI endif +ifdef VBOX_WITH_CRDUMPER +VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRDUMPER +endif +ifdef VBOX_WITH_CRSERVER_DUMPER +VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRSERVER_DUMPER +endif + # # Generate files for VBoxOGLcrserverlib @@ -165,7 +174,7 @@ $(VBOX_PATH_CROGL_GENFILES)/server_retval.c: $(addprefix $(PATH_SUB_CURRENT)/crs $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) -$(VBOX_PATH_CROGL_GENFILES)/server_get.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_get.py server_special get_components.py) $(VBOX_CROGL_API_FILES) | $$(dir $$@) +$(VBOX_PATH_CROGL_GENFILES)/server_get.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_get.py server_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) @@ -255,6 +264,12 @@ VBoxOGLrenderspu_OBJCFLAGS.darwin = -Wno-shadow VBoxOGLrenderspu_SOURCES.darwin = \ render/renderspu_cocoa.c \ render/renderspu_cocoa_helper.m +ifdef VBOX_WITH_CRHGSMI +VBoxOGLrenderspu_DEFS += VBOX_WITH_CRHGSMI +endif +ifdef VBOX_WITH_VDMA +VBoxOGLrenderspu_DEFS += VBOX_WITH_VDMA +endif VBoxOGLrenderspu_LDFLAGS.darwin += -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxOGLrenderspu.dylib VBoxOGLrenderspu_LIBS = \ $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \ diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp index 2ed4a5a3..228ccaeb 100644 --- a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,7 +31,7 @@ bool RTCALL VBoxOglIs3DAccelerationSupported() { - if (RTEnvGet("VBOX_CROGL_FORCE_SUPPORTED")) + if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED")) { LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n")); return true; diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp index a4b3a40f..9d594581 100644 --- a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2011 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp index 2f50de62..0693ed8a 100644 --- a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,7 +31,7 @@ bool RTCALL VBoxOglIs3DAccelerationSupported() { - if (RTEnvGet("VBOX_CROGL_FORCE_SUPPORTED")) + if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED")) { LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n")); return true; diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp index b63f788a..77ddf197 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp +++ b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -18,50 +18,31 @@ #define __STDC_CONSTANT_MACROS /* needed for a definition in iprt/string.h */ -#ifdef RT_OS_WINDOWS -# include <iprt/alloc.h> -# include <iprt/string.h> -# include <iprt/assert.h> -# include <iprt/stream.h> -# include <VBox/vmm/ssm.h> -# include <VBox/hgcmsvc.h> -# include <VBox/HostServices/VBoxCrOpenGLSvc.h> -# include "cr_server.h" -# define LOG_GROUP LOG_GROUP_SHARED_CROPENGL -# include <VBox/log.h> - -# include <VBox/com/com.h> -# include <VBox/com/string.h> -# include <VBox/com/array.h> -# include <VBox/com/Guid.h> -# include <VBox/com/ErrorInfo.h> -# include <VBox/com/EventQueue.h> -# include <VBox/com/VirtualBox.h> -# include <VBox/com/assert.h> - -#else -# include <VBox/com/VirtualBox.h> -# include <iprt/assert.h> -# include <VBox/vmm/ssm.h> -# include <VBox/hgcmsvc.h> -# include <VBox/HostServices/VBoxCrOpenGLSvc.h> - -# include "cr_server.h" -# define LOG_GROUP LOG_GROUP_SHARED_CROPENGL -# include <VBox/log.h> -# include <VBox/com/ErrorInfo.h> -#endif /* RT_OS_WINDOWS */ +#define LOG_GROUP LOG_GROUP_SHARED_CROPENGL -#include <VBox/com/errorprint.h> -#include <iprt/thread.h> +#include <iprt/assert.h> +#include <iprt/asm.h> #include <iprt/critsect.h> +#include <iprt/mem.h> #include <iprt/semaphore.h> -#include <iprt/asm.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/thread.h> + +#include <VBox/hgcmsvc.h> +#include <VBox/log.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/VirtualBox.h> +#include <VBox/com/errorprint.h> +#include <VBox/HostServices/VBoxCrOpenGLSvc.h> +#include <VBox/vmm/ssm.h> #include "cr_mem.h" +#include "cr_server.h" PVBOXHGCMSVCHELPERS g_pHelpers; static IConsole* g_pConsole = NULL; +static uint32_t g_u32ScreenCount = 0; static PVM g_pVM = NULL; #ifndef RT_OS_WINDOWS @@ -244,6 +225,29 @@ static int svcPresentFBOTearDown(void) return rc; } +static DECLCALLBACK(void) svcNotifyEventCB(int32_t screenId, uint32_t uEvent, void*pvData) +{ + ComPtr<IDisplay> pDisplay; + ComPtr<IFramebuffer> pFramebuffer; + LONG xo, yo; + + if (!g_pConsole) + { + crWarning("Console not defined!"); + return; + } + + CHECK_ERROR2_STMT(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), return); + + CHECK_ERROR2_STMT(pDisplay, GetFramebuffer(screenId, pFramebuffer.asOutParam(), &xo, &yo), return); + + if (!pFramebuffer) + return; + + pFramebuffer->Notify3DEvent(uEvent, (BYTE*)pvData); +} + + static DECLCALLBACK(int) svcUnload (void *) { int rc = VINF_SUCCESS; @@ -364,7 +368,7 @@ static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClie LogRel(("SHARED_CROPENGL svcLoadState: unsupported save state version %d\n", ui32)); /*@todo ugly hack, as we don't know size of stored opengl data try to read untill end of opengl data marker*/ - /*VboxSharedCrOpenGL isn't last hgcm service now, so can't use SSMR3SkipToEndOfUnit*/ + /*VBoxSharedCrOpenGL isn't last hgcm service now, so can't use SSMR3SkipToEndOfUnit*/ { const char *pMatch = &gszVBoxOGLSSMMagic[0]; char current; @@ -472,7 +476,7 @@ static CRVBOXSVCBUFFER_t* svcGetBuffer(uint32_t iBuffer, uint32_t cbBufferSize) { if (pBuffer->uiId == iBuffer) { - if (pBuffer->uiSize!=cbBufferSize) + if (cbBufferSize && pBuffer->uiSize!=cbBufferSize) { static int shown=0; @@ -892,6 +896,30 @@ static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32 break; } + case SHCRGL_GUEST_FN_GET_CAPS: + { + Log(("svcCall: SHCRGL_GUEST_FN_GET_CAPS\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_GET_CAPS) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = crVBoxServerClientGetCaps(u32ClientID, &paParms[0].u.uint32); + AssertRC(rc); + } + + break; + } + default: { rc = VERR_NOT_IMPLEMENTED; @@ -904,10 +932,36 @@ static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32 g_pHelpers->pfnCallComplete (callHandle, rc); } +static void crScreenshotHandle(CRVBOXHGCMTAKESCREENSHOT *pScreenshot, uint32_t idScreen, uint64_t u64Now) +{ + if (!pScreenshot->pfnScreenshotBegin || pScreenshot->pfnScreenshotBegin(pScreenshot->pvContext, idScreen, u64Now)) + { + CR_SCREENSHOT Screenshot; + + int rc = crServerVBoxScreenshotGet(idScreen, pScreenshot->u32Width, pScreenshot->u32Height, pScreenshot->u32Pitch, pScreenshot->pvBuffer, &Screenshot); + if (RT_SUCCESS(rc)) + { + if (pScreenshot->pfnScreenshotPerform) + pScreenshot->pfnScreenshotPerform(pScreenshot->pvContext, idScreen, + 0, 0, 32, + Screenshot.Img.pitch, Screenshot.Img.width, Screenshot.Img.height, + (uint8_t*)Screenshot.Img.pvData, u64Now); + crServerVBoxScreenshotRelease(&Screenshot); + } + else + { + Assert(rc == VERR_INVALID_STATE); + } + + if (pScreenshot->pfnScreenshotEnd) + pScreenshot->pfnScreenshotEnd(pScreenshot->pvContext, idScreen, u64Now); + } +} + /* * We differentiate between a function handler for the guest and one for the host. */ -static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +static int svcHostCallPerform(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { int rc = VINF_SUCCESS; @@ -990,6 +1044,11 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa CHECK_ERROR_BREAK(pMachine, COMGETTER(MonitorCount)(&monitorCount)); CHECK_ERROR_BREAK(pConsole, COMGETTER(Display)(pDisplay.asOutParam())); + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + g_pConsole = pConsole; + g_u32ScreenCount = monitorCount; + rc = crVBoxServerSetScreenCount(monitorCount); AssertRCReturn(rc, rc); @@ -1013,7 +1072,7 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa } } - g_pConsole = pConsole; + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); rc = VINF_SUCCESS; } @@ -1064,16 +1123,15 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa } if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pRects */ - || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* cRects */ ) { rc = VERR_INVALID_PARAMETER; break; } - Assert(sizeof(RTRECT)==4*sizeof(GLint)); + Assert(sizeof (RTRECT) == 4 * sizeof (GLint)); - rc = crVBoxServerSetRootVisibleRegion(paParms[1].u.uint32, (GLint*)paParms[0].u.pointer.addr); + rc = crVBoxServerSetRootVisibleRegion(paParms[0].u.pointer.size / sizeof (RTRECT), (const RTRECT*)paParms[0].u.pointer.addr); break; } case SHCRGL_HOST_FN_SCREEN_CHANGED: @@ -1105,6 +1163,8 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa CHECK_ERROR_RET(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), rc); CHECK_ERROR_RET(pDisplay, GetFramebuffer(screenId, pFramebuffer.asOutParam(), &xo, &yo), rc); + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + if (!pFramebuffer) { rc = crVBoxServerUnmapScreen(screenId); @@ -1112,28 +1172,130 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa } else { - CHECK_ERROR_RET(pFramebuffer, COMGETTER(WinId)(&winId), rc); +#if 0 + CHECK_ERROR_RET(pFramebuffer, Lock(), rc); +#endif - if (!winId) - { - /* View associated with framebuffer is destroyed, happens with 2d accel enabled */ - rc = crVBoxServerUnmapScreen(screenId); - AssertRCReturn(rc, rc); - } - else - { - CHECK_ERROR_RET(pFramebuffer, COMGETTER(Width)(&w), rc); - CHECK_ERROR_RET(pFramebuffer, COMGETTER(Height)(&h), rc); + do { + /* determine if the framebuffer is functional */ + rc = pFramebuffer->Notify3DEvent(VBOX3D_NOTIFY_EVENT_TYPE_TEST_FUNCTIONAL, NULL); - rc = crVBoxServerMapScreen(screenId, xo, yo, w, h, winId); - AssertRCReturn(rc, rc); - } + if (rc == S_OK) + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(WinId)(&winId)); + + if (!winId) + { + /* View associated with framebuffer is destroyed, happens with 2d accel enabled */ + rc = crVBoxServerUnmapScreen(screenId); + AssertRCReturn(rc, rc); + } + else + { + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Width)(&w)); + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Height)(&h)); + + rc = crVBoxServerMapScreen(screenId, xo, yo, w, h, winId); + AssertRCReturn(rc, rc); + } + } while (0); +#if 0 + CHECK_ERROR_RET(pFramebuffer, Unlock(), rc); +#endif } + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + rc = VINF_SUCCESS; } break; } + case SHCRGL_HOST_FN_TAKE_SCREENSHOT: + { + if (cParms != 1) + { + LogRel(("SHCRGL_HOST_FN_TAKE_SCREENSHOT: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (!paParms->u.pointer.addr) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->u.pointer.size != sizeof (CRVBOXHGCMTAKESCREENSHOT)) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)paParms->u.pointer.addr; + uint64_t u64Now = RTTimeProgramMilliTS(); + + if (pScreenshot->u32Screen == CRSCREEN_ALL) + { + for (uint32_t i = 0; i < g_u32ScreenCount; ++i) + { + crScreenshotHandle(pScreenshot, i, u64Now); + } + } + else if (pScreenshot->u32Screen < g_u32ScreenCount) + { + crScreenshotHandle(pScreenshot, pScreenshot->u32Screen, u64Now); + } + else + { + AssertMsgFailed(("invalid screen id\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + break; + } + case SHCRGL_HOST_FN_DEV_RESIZE: + { + Log(("svcCall: SHCRGL_HOST_FN_DEV_RESIZE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_DEV_RESIZE) + { + LogRel(("SHCRGL_HOST_FN_DEV_RESIZE: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + if (!paParms->u.pointer.addr) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->u.pointer.size != sizeof (CRVBOXHGCMDEVRESIZE)) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + CRVBOXHGCMDEVRESIZE *pResize = (CRVBOXHGCMDEVRESIZE*)paParms->u.pointer.addr; + + rc = crVBoxServerNotifyResize(&pResize->Screen, pResize->pvVRAM); + break; + } case SHCRGL_HOST_FN_VIEWPORT_CHANGED: { Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n")); @@ -1162,6 +1324,8 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa break; } + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + rc = crVBoxServerSetScreenViewport((int)paParms[0].u.uint32, paParms[1].u.uint32, /* x */ paParms[2].u.uint32, /* y */ @@ -1170,9 +1334,52 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa if (!RT_SUCCESS(rc)) { LogRel(("SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc)); + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + + break; + } + case SHCRGL_HOST_FN_VIEWPORT_CHANGED2: + { + Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_VIEWPORT_CHANGED) + { + LogRel(("SHCRGL_HOST_FN_VIEWPORT_CHANGED: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR + || !paParms[0].u.pointer.addr + || paParms[0].u.pointer.size != sizeof (CRVBOXHGCMVIEWPORT)) + { + LogRel(("SHCRGL_HOST_FN_VIEWPORT_CHANGED: param invalid - %d, %#x, %d", + paParms[0].type, + paParms[0].u.pointer.addr, + paParms[0].u.pointer.size)); + rc = VERR_INVALID_PARAMETER; break; } + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + CRVBOXHGCMVIEWPORT *pViewportInfo = (CRVBOXHGCMVIEWPORT*)paParms[0].u.pointer.addr; + + rc = crVBoxServerSetScreenViewport(pViewportInfo->u32Screen, + pViewportInfo->x, /* x */ + pViewportInfo->y, /* y */ + pViewportInfo->width, /* w */ + pViewportInfo->height /* h */); + if (!RT_SUCCESS(rc)) + { + LogRel(("SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc)); + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + break; } case SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT: @@ -1207,9 +1414,7 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa } else /* Execute the function. */ { - rc = crVBoxServerSetOffscreenRendering(GL_TRUE); - - if (RT_SUCCESS(rc)) + if (pOutputRedirect->H3DORBegin != NULL) { CROutputRedirect outputRedirect; outputRedirect.pvContext = pOutputRedirect->pvContext; @@ -1220,11 +1425,44 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa outputRedirect.CROREnd = pOutputRedirect->H3DOREnd; outputRedirect.CRORContextProperty = pOutputRedirect->H3DORContextProperty; rc = crVBoxServerOutputRedirectSet(&outputRedirect); + if (RT_SUCCESS(rc)) + { + rc = crVBoxServerSetOffscreenRendering(GL_TRUE); + } + } + else + { + /* Redirection is disabled. */ + crVBoxServerSetOffscreenRendering(GL_FALSE); + crVBoxServerOutputRedirectSet(NULL); } } } break; } + case SHCRGL_HOST_FN_WINDOWS_SHOW: + { + /* Verify parameter count and types. */ + if (cParms != 1) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crServerVBoxWindowsShow(paParms[0].u.uint32); + if (!RT_SUCCESS(rc)) + WARN(("crServerVBoxWindowsShow failed rc %d", rc)); + + break; + } default: rc = VERR_NOT_IMPLEMENTED; break; @@ -1234,6 +1472,73 @@ static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cPa return rc; } +int crVBoxServerHostCtl(VBOXCRCMDCTL *pCtl, uint32_t cbCtl) +{ + if ((cbCtl - sizeof (VBOXCRCMDCTL)) % sizeof(VBOXHGCMSVCPARM)) + { + WARN(("invalid param size")); + return VERR_INVALID_PARAMETER; + } + uint32_t cParams = (cbCtl - sizeof (VBOXCRCMDCTL)) / sizeof (VBOXHGCMSVCPARM); + return svcHostCallPerform(pCtl->u32Function, cParams, (VBOXHGCMSVCPARM*)(pCtl + 1)); +} + +static DECLCALLBACK(int) svcHostCall(void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + switch (u32Function) + { + case SHCRGL_HOST_FN_CTL: + { + if (cParms != 1) + { + WARN(("cParams != 1")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + WARN(("invalid param type")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->u.pointer.size < sizeof (VBOXCRCMDCTL)) + { + WARN(("invalid param size")); + return VERR_INVALID_PARAMETER; + } + + VBOXCRCMDCTL *pCtl = (VBOXCRCMDCTL*)paParms->u.pointer.addr; + switch (pCtl->enmType) + { + case VBOXCRCMDCTL_TYPE_HGCM: + { + return crVBoxServerHostCtl(pCtl, paParms->u.pointer.size); + } + case VBOXCRCMDCTL_TYPE_DISABLE: + { + if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL)) + WARN(("invalid param size")); + return crVBoxServerHgcmDisable(); + } + case VBOXCRCMDCTL_TYPE_ENABLE: + { + if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL_ENABLE)) + WARN(("invalid param size")); + VBOXCRCMDCTL_ENABLE *pEnable = (VBOXCRCMDCTL_ENABLE*)pCtl; + return crVBoxServerHgcmEnable(pEnable->hRHCmd, pEnable->pfnRHCmd); + } + default: + WARN(("invalid function")); + return VERR_INVALID_PARAMETER; + } + WARN(("should not be here!")); + return VERR_INTERNAL_ERROR; + } + default: + return svcHostCallPerform(u32Function, cParms, paParms); + } +} + extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable) { int rc = VINF_SUCCESS; @@ -1272,6 +1577,8 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *pt return VERR_NOT_SUPPORTED; rc = svcPresentFBOInit(); + + crServerVBoxSetNotifyEventCB(svcNotifyEventCB); } } diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_components.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_components.py deleted file mode 100644 index e1909d71..00000000 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_components.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) 2001, Stanford University -# All rights reserved. -# -# See the file LICENSE.txt for information on redistributing this software. - -num_components = { - 'GL_AMBIENT' : 4, - 'GL_DIFFUSE' : 4, - 'GL_SPECULAR' : 4, - 'GL_POSITION' : 4, - 'GL_SPOT_DIRECTION' : 3, - 'GL_SPOT_EXPONENT' : 1, - 'GL_SPOT_CUTOFF' : 1, - 'GL_CONSTANT_ATTENUATION' : 1, - 'GL_LINEAR_ATTENUATION' : 1, - 'GL_QUADRATIC_ATTENUATION' : 1, - 'GL_EMISSION' : 4, - 'GL_SHININESS' : 1, - 'GL_COLOR_INDEXES' : 3, - 'GL_TEXTURE_ENV_MODE' : 1, - 'GL_TEXTURE_ENV_COLOR' : 4, - 'GL_TEXTURE_GEN_MODE' : 1, - 'GL_OBJECT_PLANE' : 4, - 'GL_EYE_PLANE' : 4, - 'GL_TEXTURE_MAG_FILTER' : 1, - 'GL_TEXTURE_MIN_FILTER' : 1, - 'GL_TEXTURE_WRAP_S' : 1, - 'GL_TEXTURE_WRAP_T' : 1, - 'GL_TEXTURE_BORDER_COLOR' : 4, - 'GL_TEXTURE_WIDTH': 1, - 'GL_TEXTURE_HEIGHT': 1, - 'GL_TEXTURE_DEPTH': 1, - # 'GL_TEXTURE_INTERNAL_FORMAT': 1, THIS CONFLICTS WITH GL_TEXTURE_COMPONENTS! - 'GL_TEXTURE_BORDER': 1, - 'GL_TEXTURE_RED_SIZE': 1, - 'GL_TEXTURE_GREEN_SIZE': 1, - 'GL_TEXTURE_BLUE_SIZE': 1, - 'GL_TEXTURE_ALPHA_SIZE': 1, - 'GL_TEXTURE_LUMINANCE_SIZE': 1, - 'GL_TEXTURE_INTENSITY_SIZE': 1, - 'GL_TEXTURE_COMPONENTS': 1, - 'GL_TEXTURE_RESIDENT': 1 -} - -num_extended_components = { - 'GL_TEXTURE_MAX_ANISOTROPY_EXT': ( 1, 'CR_EXT_texture_filter_anisotropic' ), - 'GL_TEXTURE_WRAP_R': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_TEXTURE_PRIORITY': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_TEXTURE_MIN_LOD': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_TEXTURE_MAX_LOD': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_TEXTURE_BASE_LEVEL': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_TEXTURE_MAX_LEVEL': ( 1, 'CR_OPENGL_VERSION_1_2'), - 'GL_COMBINER_INPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_MAPPING_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_COMPONENT_USAGE_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_AB_DOT_PRODUCT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_CD_DOT_PRODUCT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_MUX_SUM_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_SCALE_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_BIAS_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_AB_OUTPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_CD_OUTPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_SUM_OUTPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_INPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_INPUT_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_MAPPING_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_COMBINER_COMPONENT_USAGE_NV': ( 1, 'CR_NV_register_combiners'), - 'GL_CONSTANT_COLOR0_NV': ( 4, 'CR_NV_register_combiners'), - 'GL_CONSTANT_COLOR1_NV': ( 4, 'CR_NV_register_combiners'), - 'GL_COMBINE_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_COMBINE_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE0_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE1_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE2_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE0_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE1_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_SOURCE2_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND0_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND1_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND2_RGB_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND0_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND1_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_OPERAND2_ALPHA_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_RGB_SCALE_ARB': (1, 'CR_ARB_texture_env_combine'), - 'GL_ALPHA_SCALE': (1, 'CR_ARB_texture_env_combine'), - 'GL_DEPTH_TEXTURE_MODE_ARB': (1, 'CR_ARB_depth_texture'), - 'GL_TEXTURE_DEPTH_SIZE_ARB': (1, 'CR_ARB_depth_texture'), - 'GL_TEXTURE_COMPARE_MODE_ARB': (1, 'CR_ARB_shadow'), - 'GL_TEXTURE_COMPARE_FUNC_ARB': (1, 'CR_ARB_shadow'), - 'GL_TEXTURE_COMPARE_FAIL_VALUE_ARB': (1, 'CR_ARB_shadow_ambient'), - 'GL_GENERATE_MIPMAP_SGIS': (1, 'CR_SGIS_generate_mipmap'), - 'GL_TEXTURE_LOD_BIAS_EXT': (1, 'CR_EXT_texture_lod_bias'), - 'GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB': (1, 'CR_any_vertex_program'), - 'GL_CURRENT_VERTEX_ATTRIB_ARB': (4, 'CR_any_vertex_program'), - 'GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB': (1, 'CR_any_vertex_program'), - 'GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB': (1, 'CR_any_vertex_program'), - 'GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB': (1, 'CR_any_vertex_program'), - 'GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB': (1, 'CR_any_vertex_program'), - 'GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB': (1, 'CR_any_vertex_program'), - 'GL_TRACK_MATRIX_NV': (24, 'CR_any_vertex_program'), - 'GL_TRACK_MATRIX_TRANSFORM_NV': (24, 'CR_any_vertex_program'), - 'GL_BUFFER_SIZE_ARB': (1, 'CR_ARB_vertex_buffer_object'), - 'GL_BUFFER_USAGE_ARB': (1, 'CR_ARB_vertex_buffer_object'), - 'GL_BUFFER_ACCESS_ARB': (1, 'CR_ARB_vertex_buffer_object'), - 'GL_BUFFER_MAPPED_ARB': (1, 'CR_ARB_vertex_buffer_object'), - 'GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), - 'GL_QUERY_COUNTER_BITS_ARB': (1, 'CR_ARB_occlusion_query'), - 'GL_QUERY_RESULT_AVAILABLE_ARB': (1, 'CR_ARB_occlusion_query'), - 'GL_QUERY_RESULT_ARB': (1, 'CR_ARB_occlusion_query'), - 'GL_CURRENT_QUERY_ARB': (1, 'CR_ARB_occlusion_query'), - 'GL_TEXTURE_COMPRESSED_IMAGE_SIZE': (1, 'CR_ARB_texture_compression'), - 'GL_TEXTURE_COMPRESSED': (1, 'CR_ARB_texture_compression'), - 'GL_COORD_REPLACE_ARB': (1, 'CR_ARB_point_sprite'), -} - -print """static unsigned int lookupComponents( GLenum pname ) -{ - switch( pname ) - { -""" -comps = num_components.keys(); -comps.sort(); -for comp in comps: - print '\t\t\tcase %s: return %d;' % (comp,num_components[comp]) - -comps = num_extended_components.keys(); -comps.sort(); -for comp in comps: - (nc, ifdef) = num_extended_components[comp] - print '#ifdef %s' % ifdef - print '\t\t\tcase %s: return %d;' % (comp,nc) - print '#endif /* %s */' % ifdef - -print """ - default: - crError( "Unknown parameter name in lookupComponents: %d", (int) pname ); - break; - } - /* NOTREACHED */ - return 0; -} -""" - - diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py index 8475344a..f8d281dc 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py @@ -393,8 +393,10 @@ extensions_num_get_values = { # Point sprite (2.0) # 'GL_POINT_SPRITE': (1, 'CR_OPENGL_VERSION_2_0'), # Separate stencil (2.0) # - 'GL_STENCIL_BACK_FAIL': (1, 'CR_OPENGL_VERSION_2_0'), 'GL_STENCIL_BACK_FUNC': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_REF': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_VALUE_MASK': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_FAIL': (1, 'CR_OPENGL_VERSION_2_0'), 'GL_STENCIL_BACK_PASS_DEPTH_FAIL': (1, 'CR_OPENGL_VERSION_2_0'), 'GL_STENCIL_BACK_PASS_DEPTH_PASS': (1, 'CR_OPENGL_VERSION_2_0'), # Frame buffer object EXT # @@ -404,22 +406,52 @@ extensions_num_get_values = { 'GL_MAX_RENDERBUFFER_SIZE_EXT': (1, 'CR_EXT_framebuffer_object'), # ARB_shader_objects 'GL_CURRENT_PROGRAM': (1, 'CR_ARB_shader_objects'), + # EXT_framebuffer_blit + 'GL_READ_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'), + 'GL_DRAW_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'), + # EXT_stencil_two_side + 'GL_ACTIVE_STENCIL_FACE_EXT': (1, 'CR_EXT_stencil_two_side'), } get_keys = num_get_values.keys() + extensions_num_get_values.keys() get_keys.sort() +max_keyvalues = 0 -print "struct nv_struct { GLenum pname; unsigned int num_values; } num_values_array[] = {" +print """ +static struct nv_struct { GLenum pname; unsigned int num_values; +#ifdef VBOX_WITH_CRDUMPER +const char* pszName; +#endif +} num_values_array[] = { +""" for key in get_keys: try: - print '\t{ %s, %d },' % (key, num_get_values[key]) + keyvalues = num_get_values[key] + if max_keyvalues < keyvalues: + max_keyvalues = keyvalues + print """ + \t{ %s, %d +#ifdef VBOX_WITH_CRDUMPER + , "%s" +#endif + }, + """ % (key, keyvalues, key) except KeyError: (nv, ifdef) = extensions_num_get_values[key] + if max_keyvalues < nv: + max_keyvalues = nv print '#ifdef %s' % ifdef - print '\t{ %s, %d },' % (key, nv) + print """ + \t{ %s, %d + #ifdef VBOX_WITH_CRDUMPER + , "%s" + #endif + }, + """ % (key, nv, key) print '#endif /* %s */' % ifdef print "\t{ 0, 0 }" print "};" +print "#define CR_MAX_GET_VALUES %d" % max_keyvalues print """ static unsigned int __numValues( GLenum pname ) diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h index 4e14a86e..677bcf0e 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h @@ -14,10 +14,16 @@ #include "state/cr_currentpointers.h" #include "cr_server.h" +#include <cr_htable.h> +#include <cr_compositor.h> #ifdef VBOX_WITH_CRHGSMI # include <VBox/VBoxVideo.h> +#include <iprt/cdefs.h> + +RT_C_DECLS_BEGIN + extern uint8_t* g_pvVRamBase; extern uint32_t g_cbVRam; extern HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion; @@ -37,9 +43,10 @@ DECLINLINE(void) crServerCrHgsmiCmdComplete(struct VBOXVDMACMD_CHROMIUM_CMD *pCm } #define VBOXCRHGSMI_CMD_COMPLETE(_pData, _rc) do { \ + Assert(CRVBOXHGSMI_CMDDATA_IS_HGSMICMD(_pData)); \ CRVBOXHGSMI_CMDDATA_ASSERT_ISSET(_pData); \ CRVBOXHGSMI_CMDDATA_RC(_pData, _rc); \ - crServerCrHgsmiCmdComplete((_pData)->pCmd, VINF_SUCCESS); \ + crServerCrHgsmiCmdComplete((_pData)->pHgsmiCmd, VINF_SUCCESS); \ } while (0) #define VBOXCRHGSMI_CMD_CHECK_COMPLETE(_pData, _rc) do { \ @@ -108,8 +115,12 @@ GLboolean crServerClientInBeginEnd(const CRClient *client); GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLint shareCtx, GLint preloadCtxID, int32_t internalID); GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID); - -void crServerCreateInfoDeleteCB(void *data); +GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID); +void crServerMuralTerm(CRMuralInfo *mural); +GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height); +void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y); +void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects ); +void crServerMuralShow( CRMuralInfo *mural, GLint state ); GLint crServerGenerateID(GLint *pCounter); @@ -117,15 +128,577 @@ GLint crServerSPUWindowID(GLint serverWindow); GLuint crServerTranslateProgramID(GLuint id); -void crServerSetupOutputRedirect(CRMuralInfo *mural); +CRMuralInfo * crServerGetDummyMural(GLint visualBits); + void crServerCheckMuralGeometry(CRMuralInfo *mural); +void crServerCheckAllMuralGeometry(CRMuralInfo *pMI); GLboolean crServerSupportRedirMuralFBO(void); -void crServerRedirMuralFBO(CRMuralInfo *mural, GLboolean redir); -void crServerCreateMuralFBO(CRMuralInfo *mural); + +void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb); +void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb); + +void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void*pvData); + +void crServerRedirMuralFbClear(CRMuralInfo *mural); + +void crServerWindowReparent(CRMuralInfo *pMural); + +void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled); void crServerDeleteMuralFBO(CRMuralInfo *mural); void crServerPresentFBO(CRMuralInfo *mural); GLboolean crServerIsRedirectedToFBO(); +GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer); +void crServerMuralFBOSwapBuffers(CRMuralInfo *mural); + +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled(); +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb); +HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized(); +HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb); + +int CrFbRegionsClear(HCR_FRAMEBUFFER hFb); + + +#define CR_SERVER_FBO_BB_IDX(_mural) ((_mural)->iBbBuffer) +#define CR_SERVER_FBO_FB_IDX(_mural) (((_mural)->iBbBuffer + 1) % ((_mural)->cBuffers)) +/* returns a valid index to be used for negative _idx, i.e. for GL_NONE cases */ +//#define CR_SERVER_FBO_ADJUST_IDX(_mural, _idx) ((_idx) >= 0 ? (_idx) : CR_SERVER_FBO_BB_IDX(_mural)) +/* just a helper that uses CR_SERVER_FBO_ADJUST_IDX for getting mural's FBO id for buffer index*/ +//#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_mural)->aidFBOs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))]) +//#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_mural)->aidColorTexs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))]) +#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidFBOs[(_idx)] : 0) +#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidColorTexs[(_idx)] : 0) int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer); +void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo ); + +PCR_BLITTER crServerVBoxBlitterGet(); +PCR_BLITTER crServerVBoxBlitterGetInitialized(); + +DECLINLINE(void) crServerVBoxBlitterWinInit(CR_BLITTER_WINDOW *win, CRMuralInfo *mural) +{ + win->Base.id = mural->spuWindow; + win->Base.visualBits = mural->CreateInfo.realVisualBits; + win->width = mural->width; + win->height = mural->height; +} + +DECLINLINE(void) crServerVBoxBlitterCtxInit(CR_BLITTER_CONTEXT *ctx, CRContextInfo *ctxInfo) +{ + ctx->Base.id = ctxInfo->SpuContext; + if (ctx->Base.id < 0) + ctx->Base.id = cr_server.MainContextInfo.SpuContext; + ctx->Base.visualBits = cr_server.curClient->currentCtxInfo->CreateInfo.realVisualBits; +} + +/* display worker thread. + * see comments for CR_SERVER_RPW struct definition in cr_server.h */ +DECLINLINE(void) crServerXchgI8(int8_t *pu8Val1, int8_t *pu8Val2) +{ + int8_t tmp; + tmp = *pu8Val1; + *pu8Val1 = *pu8Val2; + *pu8Val2 = tmp; +} + +#ifdef DEBUG +# define CR_GLERR_CHECK(_op) do { \ + GLenum status; \ + while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {/*Assert(0);*/} \ + _op \ + while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {Assert(0);} \ + } while (0) +#else +# define CR_GLERR_CHECK(_op) do { \ + _op \ + } while (0) +#endif + +#ifdef DEBUG_misha +# define CR_SERVER_RPW_DEBUG +#endif +/* * + * _name : Draw, Submitted, Worker, Gpu + */ + +#ifdef CR_SERVER_RPW_DEBUG +# define crServerRpwEntryDbgVerify(_pE) crServerRpwEntryDbgDoVerify(_pE) +#else +# define crServerRpwEntryDbgVerify(_pE) do {} while (0) +#endif + + +#define CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name) ((_pEntry)->iTex##_name > 0) + +#define CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(_pEntry, _name) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name)); \ + (_pEntry)->iTex##_name = -(_pEntry)->iTex##_name; \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + + +#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + (_pEntry)->iTex##_fromName = -(_pEntry)->iTex##_fromName; \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX(_pEntry, _name) ((_pEntry)->aidWorkerTexs[(_pEntry)->iTex##_name - 1]) + +#define CR_SERVER_RPW_ENTRY_PBO_NEXT_ID(_i) (((_i) + 1) % 2) +#define CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(_pEntry) ((_pEntry)->iCurPBO >= 0) +#define CR_SERVER_RPW_ENTRY_PBO_CUR(_pEntry) ((_pEntry)->aidPBOs[(_pEntry)->iCurPBO]) +#define CR_SERVER_RPW_ENTRY_PBO_COMPLETED(_pEntry) ((_pEntry)->aidPBOs[CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO)]) +#define CR_SERVER_RPW_ENTRY_PBO_FLIP(_pEntry) do { \ + (_pEntry)->iCurPBO = CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO); \ + } while (0) + +#ifdef CR_SERVER_RPW_DEBUG +DECLINLINE(void) crServerRpwEntryDbgDoVerify(CR_SERVER_RPW_ENTRY *pEntry) +{ + int tstMask = 0; + int8_t iVal; + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)); + +#define CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(_v) do { \ + iVal = RT_ABS(_v); \ + Assert(iVal > 0); \ + Assert(iVal < 5); \ + Assert(!(tstMask & (1 << iVal))); \ + tstMask |= (1 << iVal); \ + } while (0) + + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexDraw); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexSubmitted); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexWorker); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexGpu); + Assert(tstMask == 0x1E); +} +#endif + +DECLINLINE(bool) crServerRpwIsInitialized(const CR_SERVER_RPW *pWorker) +{ + return !!pWorker->ctxId; +} +int crServerRpwInit(CR_SERVER_RPW *pWorker); +int crServerRpwTerm(CR_SERVER_RPW *pWorker); +DECLINLINE(bool) crServerRpwEntryIsInitialized(const CR_SERVER_RPW_ENTRY *pEntry) +{ + return !!pEntry->pfnData; +} +int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData); +int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height); +int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +DECLINLINE(void) crServerRpwEntryDrawSettingsToTex(const CR_SERVER_RPW_ENTRY *pEntry, VBOXVR_TEXTURE *pTex) +{ + pTex->width = pEntry->Size.cx; + pTex->height = pEntry->Size.cy; + pTex->target = GL_TEXTURE_2D; + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)); + pTex->hwid = CR_SERVER_RPW_ENTRY_TEX(pEntry, Draw); +} +/**/ + +typedef struct CR_SERVER_CTX_SWITCH +{ + GLuint idDrawFBO, idReadFBO; + CRContext *pNewCtx; + CRContext *pOldCtx; +} CR_SERVER_CTX_SWITCH; + +DECLINLINE(void) crServerCtxSwitchPrepare(CR_SERVER_CTX_SWITCH *pData, CRContext *pNewCtx) +{ + CRMuralInfo *pCurrentMural = cr_server.currentMural; + CRContextInfo *pCurCtxInfo = cr_server.currentCtxInfo; + GLuint idDrawFBO, idReadFBO; + CRContext *pCurCtx = pCurCtxInfo ? pCurCtxInfo->pContext : NULL; + + CRASSERT(pCurCtx == crStateGetCurrent()); + + if (pCurrentMural) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + + crStateSwitchPrepare(pNewCtx, pCurCtx, idDrawFBO, idReadFBO); + + pData->idDrawFBO = idDrawFBO; + pData->idReadFBO = idReadFBO; + pData->pNewCtx = pNewCtx; + pData->pOldCtx = pCurCtx; +} + +DECLINLINE(void) crServerCtxSwitchPostprocess(CR_SERVER_CTX_SWITCH *pData) +{ + crStateSwitchPostprocess(pData->pOldCtx, pData->pNewCtx, pData->idDrawFBO, pData->idReadFBO); +} + +void crServerInitTmpCtxDispatch(); + +int crServerVBoxParseNumerics(const char *pszStr, const int defaultVal); + +typedef struct CR_FBMAP +{ + uint8_t Map[(CR_MAX_GUEST_MONITORS+7)/8]; +} CR_FBMAP; + +DECLINLINE(void) CrFBmInit(CR_FBMAP *pMap) +{ + memset(pMap, 0, sizeof (*pMap)); +} + +DECLINLINE(bool) CrFBmIsSet(CR_FBMAP *pMap, uint32_t i) +{ + return ASMBitTest(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmSet(CR_FBMAP *pMap, uint32_t i) +{ + ASMBitSet(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmSetAtomic(CR_FBMAP *pMap, uint32_t i) +{ + ASMAtomicBitSet(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmClear(CR_FBMAP *pMap, uint32_t i) +{ + ASMBitClear(&pMap->Map, i); +} + +/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */ +int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap); +/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */ +void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap); +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled(); +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb); +HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idScreen); +int CrPMgrModeVrdp(bool fEnable); +int CrPMgrModeRootVr(bool fEnable); +int CrPMgrModeWinVisible(bool fEnable); +int CrPMgrRootVrUpdate(); +int CrPMgrViewportUpdate(uint32_t idScreen); +int CrPMgrScreenChanged(uint32_t idScreen); +int CrPMgrNotifyResize(HCR_FRAMEBUFFER hFb); +int CrPMgrSaveState(PSSMHANDLE pSSM); +int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version); +HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idScreen); +/*cleanup stuff*/ + +int CrPMgrInit(); +void CrPMgrTerm(); + +typedef DECLCALLBACKPTR(bool, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + +bool CrFbHas3DData(HCR_FRAMEBUFFER hFb); +void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext); +int CrFbResize(HCR_FRAMEBUFFER hFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM); +int CrFbBltGetContents(HCR_FRAMEBUFFER hFb, const RTRECT *pSrcRect, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pPrects, CR_BLITTER_IMG *pImg); +bool CrFbIsEnabled(HCR_FRAMEBUFFER hFb); +int CrFbEntryCreateForTexId(HCR_FRAMEBUFFER hFb, GLuint idTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry); +int CrFbEntryCreateForTexData(HCR_FRAMEBUFFER hFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry); +void CrFbEntryAddRef(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); +void CrFbEntryRelease(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); +const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb); +void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb); +const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(HCR_FRAMEBUFFER hFb); +const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry); + +/* start doing modifications to the framebuffer */ +int CrFbUpdateBegin(HCR_FRAMEBUFFER hFb); +/*below commands can only be used in Framebuffer update mode, i.e. after the CrFbUpdateBegin succeeded */ +int CrFbEntryRegions(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); + +/* complete doing modifications to the framebuffer */ +void CrFbUpdateEnd(HCR_FRAMEBUFFER hFb); + +int CrFbEntryRegionsAdd(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated); +int CrFbEntryRegionsSet(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated); + +int CrFbEntryTexDataUpdate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex); + +CRHTABLE_HANDLE CrFbDDataAllocSlot(HCR_FRAMEBUFFER hFb); + +typedef DECLCALLBACKPTR(void, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + +void CrFbDDataReleaseSlot(HCR_FRAMEBUFFER hFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext); +int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData); +void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot); +void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot); + +CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex); +void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased); + +int32_t crVBoxServerCrCmdBltProcess(PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd); + +//#define VBOX_WITH_CRSERVER_DUMPER +#ifdef VBOX_WITH_CRSERVER_DUMPER +void crServerDumpCheckTerm(); +int crServerDumpCheckInit(); +void crServerDumpBuffer(int idx); +void crServerDumpTextures(); +void crServerDumpTexture(const VBOXVR_TEXTURE *pTex); +void crServerDumpShader(GLint id); +void crServerDumpProgram(GLint id); +void crServerDumpCurrentProgram(); +void crServerDumpRecompileDumpCurrentProgram(); +void crServerRecompileCurrentProgram(); +void crServerDumpCurrentProgramUniforms(); +void crServerDumpCurrentProgramAttribs(); +void crServerDumpFramesCheck(); +void crServerDumpState(); +void crServerDumpDrawel(const char*pszFormat, ...); +void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal); + +extern int64_t g_CrDbgDumpPid; +extern unsigned long g_CrDbgDumpEnabled; +extern unsigned long g_CrDbgDumpDraw; +extern unsigned long g_CrDbgDumpDrawFramesSettings; +extern unsigned long g_CrDbgDumpDrawFramesAppliedSettings; +extern unsigned long g_CrDbgDumpDrawFramesCount; + +extern uint32_t g_CrDbgDumpVertattrFixupOn; + +bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper); +bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper); +void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper); + +//#define CR_SERVER_DUMP_MASK_OP 0x0000fffc +//#define CR_SERVER_DUMP_OFF_OP 2 +// +//#define CR_SERVER_DUMP_MASK_DIR 0x00000003 +//#define CR_SERVER_DUMP_OFF_DIR 0 +// +//#define CR_SERVER_DUMP_MASK_DMP 0xffff0000 +//#define CR_SERVER_DUMP_OFF_DMP 16 +// +//#define CR_SERVER_DUMP_MAKE_OP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_OP)) +//#define CR_SERVER_DUMP_MAKE_DIR(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DIR)) +//#define CR_SERVER_DUMP_MAKE_DMP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DMP)) +// +//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP) +//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP) +//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR) +// +//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2))) +//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2))) +//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2))) +// +//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) ((CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2)) == CR_SERVER_DUMP_GET_OP(_v2)) +//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) ((CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2)) == CR_SERVER_DUMP_GET_DIR(_v2)) +//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) ((CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2)) == CR_SERVER_DUMP_GET_DMP(_v2)) +// +//#define CR_SERVER_DUMP_F_DIR_ENTER CR_SERVER_DUMP_MAKE_DIR(0) +//#define CR_SERVER_DUMP_F_DIR_LEAVE CR_SERVER_DUMP_MAKE_DIR(1) +// +//#define CR_SERVER_DUMP_F_OP_DRAW CR_SERVER_DUMP_MAKE_OP(0) +//#define CR_SERVER_DUMP_F_OP_SWAPBUFFERS CR_SERVER_DUMP_MAKE_OP(1) +//#define CR_SERVER_DUMP_F_OP_LINK_PROGRAM CR_SERVER_DUMP_MAKE_OP(2) +//#define CR_SERVER_DUMP_F_OP_COMPILE_PROGRAM CR_SERVER_DUMP_MAKE_OP(3) +// +//#define CR_SERVER_DUMP_F_DMP_BUFF CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_TEX CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_PROGRAM CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_PROGRAM_UNIFORMS CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_STATE CR_SERVER_DUMP_MAKE_DMP(0) +// +//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP) +//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP) +//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR) + +#define CR_SERVER_DUMP_F_DRAW_BUFF_ENTER 0x00000001 +#define CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE 0x00000002 +#define CR_SERVER_DUMP_F_DRAW_STATE_ENTER 0x00000004 +#define CR_SERVER_DUMP_F_DRAW_STATE_LEAVE 0x00000008 +#define CR_SERVER_DUMP_F_DRAW_TEX_ENTER 0x00000010 +#define CR_SERVER_DUMP_F_DRAW_TEX_LEAVE 0x00000020 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER 0x00000040 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE 0x00000080 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER 0x00000100 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE 0x00000200 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER 0x00000400 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE 0x00000800 + +#define CR_SERVER_DUMP_F_DRAW_ENTER_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_ENTER \ + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER \ + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER) + +#define CR_SERVER_DUMP_F_DRAW_LEAVE_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_TEX_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_STATE_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE) + +#define CR_SERVER_DUMP_F_DRAW_ALL (CR_SERVER_DUMP_F_DRAW_ENTER_ALL | CR_SERVER_DUMP_F_DRAW_LEAVE_ALL) + +#define CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER 0x00010000 +#define CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE 0x00020000 +#define CR_SERVER_DUMP_F_TEXPRESENT 0x00040000 +#define CR_SERVER_DUMP_F_DRAWEL 0x00100000 +#define CR_SERVER_DUMP_F_COMPILE_SHADER 0x01000000 +#define CR_SERVER_DUMP_F_SHADER_SOURCE 0x02000000 +#define CR_SERVER_DUMP_F_LINK_PROGRAM 0x04000000 + + +#define CR_SERVER_DUMP_DEFAULT_FILTER_OP(_ev) ((((_ev) & g_CrDbgDumpDraw) != 0) \ + || ((_ev) == CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER && g_CrDbgDumpDrawFramesCount)) + +#define CR_SERVER_DUMP_DEFAULT_FILTER_DMP(_ev) (((_ev) & g_CrDbgDumpDraw) != 0) + +#define CR_SERVER_DUMP_FILTER_OP(_ev, _pDumper) (g_CrDbgDumpEnabled \ + && (!g_CrDbgDumpPid \ + || (g_CrDbgDumpPid > 0 && ((uint64_t)g_CrDbgDumpPid) == cr_server.curClient->pid) \ + || (g_CrDbgDumpPid < 0 && ((uint64_t)(-g_CrDbgDumpPid)) != cr_server.curClient->pid)) \ + && crServerDumpFilterOpEnter((_ev), (_pDumper))) +#define CR_SERVER_DUMP_FILTER_DMP(_ev, _pDumper) (crServerDumpFilterDmp((_ev), (_pDumper))) + +#define CR_SERVER_DUMP_DRAW_ENTER() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_ENTER, cr_server.Recorder.pDumper)) { crServerDumpState(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_ENTER, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAW_LEAVE() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpState(); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpShader((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpShader((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpProgram((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(CR_SERVER_FBO_BB_IDX(cr_server.currentMural)); } \ + if (g_CrDbgDumpDrawFramesCount) { crServerDumpFramesCheck(); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpTexture((_pTex)); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper)) break; \ + crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAWEL_F(_msg) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpDrawel _msg; \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpDrawelv((_index), (_pszElFormat), (_cbEl), (_pvVal), (_cVal)); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \ + } while (0) +#else /* if !defined VBOX_WITH_CRSERVER_DUMPER */ +#define CR_SERVER_DUMP_DRAW_ENTER() do {} while (0) +#define CR_SERVER_DUMP_DRAW_LEAVE() do {} while (0) +#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do {} while (0) +#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do {} while (0) +#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do {} while (0) +#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do {} while (0) +#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do {} while (0) +#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do {} while (0) +#define CR_SERVER_DUMP_DRAWEL_F(_msg) do {} while (0) +#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do {} while (0) +#endif /* !VBOX_WITH_CRSERVER_DUMPER */ + +RT_C_DECLS_END + #endif /* CR_SERVER_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c index 9210d83b..e3c0100c 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c @@ -27,11 +27,18 @@ crServerDispatchGenBuffersARB(GLsizei n, GLuint *buffers) { GLuint *local_buffers = (GLuint *) crAlloc( n * sizeof(*local_buffers) ); (void) buffers; - cr_server.head_spu->dispatch_table.GenBuffersARB( n, local_buffers ); + + crStateGenBuffersARB(n, local_buffers); + crServerReturnValue( local_buffers, n * sizeof(*local_buffers) ); crFree( local_buffers ); } +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteBuffersARB( GLsizei n, const GLuint * buffer ) +{ + crStateDeleteBuffersARB( n, buffer ); +} + void SERVER_DISPATCH_APIENTRY crServerDispatchGetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params) { @@ -69,8 +76,9 @@ crServerDispatchBindBufferARB(GLenum target, GLuint buffer) GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsBufferARB(GLuint buffer) { - GLboolean retval; - retval = cr_server.head_spu->dispatch_table.IsBufferARB(crStateGetBufferHWID(buffer)); + /* since GenBuffersARB issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsBufferARB(buffer); crServerReturnValue( &retval, sizeof(retval) ); return retval; /* WILL PROBABLY BE IGNORED */ } diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c index a6e6103f..64ef9462 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c @@ -427,18 +427,25 @@ crServerDispatchSwapBuffers( GLint window, GLint flags ) ctx = crStateGetCurrent(); + CRASSERT(cr_server.curClient && cr_server.curClient->currentMural == mural); + if (ctx->framebufferobject.drawFB || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) - cr_server.curClient->currentMural->bFbDraw = GL_FALSE; + mural->bFbDraw = GL_FALSE; + + CR_SERVER_DUMP_SWAPBUFFERS_ENTER(); if (crServerIsRedirectedToFBO()) { + crServerMuralFBOSwapBuffers(mural); crServerPresentFBO(mural); } else { cr_server.head_spu->dispatch_table.SwapBuffers( mural->spuWindow, flags ); } + + CR_SERVER_DUMP_SWAPBUFFERS_LEAVE(); } void SERVER_DISPATCH_APIENTRY @@ -447,20 +454,19 @@ crServerDispatchFlush(void) CRContext *ctx = crStateGetCurrent(); cr_server.head_spu->dispatch_table.Flush(); - if (!cr_server.curClient->currentMural) /* <- on window destroy this will be zero */ - return; - - if (cr_server.curClient->currentMural->bFbDraw && crServerIsRedirectedToFBO()) + if (cr_server.curClient && cr_server.curClient->currentMural) { -#ifdef DEBUG_misha - CRASSERT(0); -#endif - crServerPresentFBO(cr_server.curClient->currentMural); - } + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (mural->bFbDraw) + { + if (crServerIsRedirectedToFBO()) + crServerPresentFBO(mural); + } - if (ctx->framebufferobject.drawFB - || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) - cr_server.curClient->currentMural->bFbDraw = GL_FALSE; + if (ctx->framebufferobject.drawFB + || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) + mural->bFbDraw = GL_FALSE; + } } void SERVER_DISPATCH_APIENTRY @@ -470,15 +476,17 @@ crServerDispatchFinish(void) cr_server.head_spu->dispatch_table.Finish(); - if (cr_server.curClient->currentMural->bFbDraw && crServerIsRedirectedToFBO()) + if (cr_server.curClient && cr_server.curClient->currentMural) { -#ifdef DEBUG_misha - CRASSERT(0); -#endif - crServerPresentFBO(cr_server.curClient->currentMural); - } + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (mural->bFbDraw) + { + if (crServerIsRedirectedToFBO()) + crServerPresentFBO(mural); + } - if (ctx->framebufferobject.drawFB - || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) - cr_server.curClient->currentMural->bFbDraw = GL_FALSE; + if (ctx->framebufferobject.drawFB + || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) + mural->bFbDraw = GL_FALSE; + } } diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c index 1621dcfc..c4b0ab93 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c @@ -50,14 +50,67 @@ setDefaults(void) cr_server.uniqueWindows = 0; - cr_server.idsPool.freeWindowID = 1; - cr_server.idsPool.freeContextID = 1; - cr_server.idsPool.freeClientID = 1; - cr_server.screenCount = 0; - cr_server.bForceOffscreenRendering = GL_FALSE; cr_server.bUsePBOForReadback = GL_FALSE; - cr_server.bUseOutputRedirect = GL_FALSE; + cr_server.bWindowsInitiallyHidden = GL_FALSE; + + cr_server.pfnNotifyEventCB = NULL; +} + +int crServerVBoxParseNumerics(const char *pszStr, const int defaultVal) +{ + int result = 0; + bool neg = false; + unsigned char iDigit = 0; + if (!pszStr || pszStr[0] == '\0') + return defaultVal; + + for (;;) + { + if (pszStr[0] == '\0') + return defaultVal; + + if (pszStr[0] == ' ' || pszStr[0] == '\t' || pszStr[0] == '\n') + { + ++pszStr; + continue; + } + + if (pszStr[0] == '-') + { + if (neg) + return defaultVal; + + neg = true; + ++pszStr; + continue; + } + + break; + } + + for (;;) + { + unsigned char digit; + if (pszStr[0] == '\0') + { + if (!iDigit) + return defaultVal; + break; + } + + digit = pszStr[0] - '0'; + if (digit > 9) + return defaultVal; + + result *= 10; + result += digit; + ++iDigit; + + ++pszStr; + } + + return !neg ? result : -result; } void crServerSetVBoxConfiguration() @@ -79,6 +132,7 @@ void crServerSetVBoxConfiguration() char hostname[1024]; char **clientchain, **clientlist; GLint dims[4]; + const char * env; defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); CRASSERT(defaultMural); @@ -155,6 +209,37 @@ void crServerSetVBoxConfiguration() cr_server.head_spu = crSPULoadChain(num_spus, spu_ids, spu_names, spu_dir, &cr_server); + env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" ); + if (env != NULL && env[0] != '\0') + { + unsigned int bits = (unsigned int)crServerVBoxParseNumerics(env, 0); + if (bits <= CR_ALL_BITS) + cr_server.fVisualBitsDefault = bits; + else + crWarning("invalid bits option %c", bits); + } + else + cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT; + + env = crGetenv("CR_SERVER_CAPS"); + if (env && env[0] != '\0') + { + cr_server.u32Caps = crServerVBoxParseNumerics(env, 0); + cr_server.u32Caps &= ~(CR_VBOX_CAP_TEX_PRESENT | CR_VBOX_CAP_CMDVBVA); + } + else + { + cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT/* | CR_VBOX_CAP_CMDVBVA*/; +#ifdef DEBUG_misha + cr_server.u32Caps |= CR_VBOX_CAP_CMDVBVA; +#endif + + } + + crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)", + cr_server.u32Caps, + cr_server.fVisualBitsDefault); + /* Need to do this as early as possible */ cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]); @@ -258,6 +343,7 @@ void crServerSetVBoxConfigurationHGCM() char *spu_dir = NULL; int i; GLint dims[4]; + const char * env; defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); CRASSERT(defaultMural); @@ -272,6 +358,37 @@ void crServerSetVBoxConfigurationHGCM() if (!cr_server.head_spu) return; + + env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" ); + if (env != NULL && env[0] != '\0') + { + unsigned int bits = (unsigned int)crServerVBoxParseNumerics(env, 0); + if (bits <= CR_ALL_BITS) + cr_server.fVisualBitsDefault = bits; + else + crWarning("invalid bits option %c", bits); + } + else + cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT; + + env = crGetenv("CR_SERVER_CAPS"); + if (env && env[0] != '\0') + { + cr_server.u32Caps = crServerVBoxParseNumerics(env, 0); + cr_server.u32Caps &= ~(CR_VBOX_CAP_TEX_PRESENT | CR_VBOX_CAP_CMDVBVA); + } + else + { + cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT/* | CR_VBOX_CAP_CMDVBVA*/; +#ifdef DEBUG_misha + cr_server.u32Caps |= CR_VBOX_CAP_CMDVBVA; +#endif + } + + crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)", + cr_server.u32Caps, + cr_server.fVisualBitsDefault); + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]); cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, &dims[2]); diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c index 5c2ce040..afa2a208 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c @@ -27,6 +27,8 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi CRContextInfo *pContextInfo; GLboolean fFirst = GL_FALSE; + dpyName = ""; + if (shareCtx > 0) { crWarning("CRServer: context sharing not implemented."); shareCtx = 0; @@ -39,16 +41,23 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi return -1; } - pContextInfo->CreateInfo.visualBits = visualBits; + pContextInfo->currentMural = NULL; + + pContextInfo->CreateInfo.requestedVisualBits = visualBits; + + if (cr_server.fVisualBitsDefault) + visualBits = cr_server.fVisualBitsDefault; + + pContextInfo->CreateInfo.realVisualBits = visualBits; /* Since the Cr server serialized all incoming clients/contexts into * one outgoing GL stream, we only need to create one context for the * head SPU. We'll only have to make it current once too, below. */ if (cr_server.firstCallCreateContext) { - cr_server.MainContextInfo.CreateInfo.visualBits = visualBits; + cr_server.MainContextInfo.CreateInfo.realVisualBits = visualBits; cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table. - CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.visualBits, shareCtx); + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, shareCtx); if (cr_server.MainContextInfo.SpuContext < 0) { crWarning("crServerDispatchCreateContext() failed."); crFree(pContextInfo); @@ -58,16 +67,19 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi CRASSERT(cr_server.MainContextInfo.pContext); cr_server.firstCallCreateContext = GL_FALSE; fFirst = GL_TRUE; + + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext); } else { /* second or third or ... context */ - if (!cr_server.bUseMultipleContexts && ((visualBits & cr_server.MainContextInfo.CreateInfo.visualBits) != visualBits)) { + if (!cr_server.bUseMultipleContexts && ((visualBits & cr_server.MainContextInfo.CreateInfo.realVisualBits) != visualBits)) { int oldSpuContext; - + /* should never be here */ + CRASSERT(0); /* the new context needs new visual attributes */ - cr_server.MainContextInfo.CreateInfo.visualBits |= visualBits; - crDebug("crServerDispatchCreateContext requires new visual (0x%x).", - cr_server.MainContextInfo.CreateInfo.visualBits); + cr_server.MainContextInfo.CreateInfo.realVisualBits |= visualBits; + crWarning("crServerDispatchCreateContext requires new visual (0x%x).", + cr_server.MainContextInfo.CreateInfo.realVisualBits); /* Here, we used to just destroy the old rendering context. * Unfortunately, this had the side effect of destroying @@ -82,7 +94,7 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi /* create new rendering context with suitable visual */ oldSpuContext = cr_server.MainContextInfo.SpuContext; cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table. - CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.visualBits, cr_server.MainContextInfo.SpuContext); + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); /* destroy old rendering context */ cr_server.head_spu->dispatch_table.DestroyContext(oldSpuContext); if (cr_server.MainContextInfo.SpuContext < 0) { @@ -90,12 +102,16 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi crFree(pContextInfo); return -1; } + + /* we do not need to clean up the old default context explicitly, since the above cr_server.head_spu->dispatch_table.DestroyContext call + * will do that for us */ + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext); } } if (cr_server.bUseMultipleContexts) { pContextInfo->SpuContext = cr_server.head_spu->dispatch_table. - CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.visualBits, cr_server.MainContextInfo.SpuContext); + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); if (pContextInfo->SpuContext < 0) { crWarning("crServerDispatchCreateContext() failed."); crStateEnableDiffOnMakeCurrent(GL_TRUE); @@ -120,10 +136,10 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi if (newCtx) { crStateSetCurrentPointers( newCtx, &(cr_server.current) ); crStateResetCurrentPointers(&(cr_server.current)); - retVal = preloadCtxID<0 ? crServerGenerateID(&cr_server.idsPool.freeContextID) : preloadCtxID; + retVal = preloadCtxID<0 ? (GLint)crHashtableAllocKeys( cr_server.contextTable, 1 ) : preloadCtxID; pContextInfo->pContext = newCtx; - pContextInfo->CreateInfo.visualBits = visualBits; + Assert(pContextInfo->CreateInfo.realVisualBits == visualBits); pContextInfo->CreateInfo.externalID = retVal; pContextInfo->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL; crHashtableAdd(cr_server.contextTable, retVal, pContextInfo); @@ -139,25 +155,6 @@ GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLi } } - { - /* As we're using only one host context to serve all client contexts, newly created context will still - * hold last error value from any previous failed opengl call. Proper solution would be to redirect any - * client glGetError calls to our state tracker, but right now it's missing quite a lot of checks and doesn't - * reflect host driver/gpu specific issues. Thus we just reset last opengl error at context creation. - */ - GLint err; - - err = cr_server.head_spu->dispatch_table.GetError(); - if (err!=GL_NO_ERROR) - { -#ifdef DEBUG_misha - crDebug("Cleared gl error %#x on context creation", err); -#else - crWarning("Cleared gl error %#x on context creation", err); -#endif - } - } - crServerReturnValue( &retVal, sizeof(retVal) ); return retVal; @@ -179,6 +176,14 @@ static int crServerRemoveClientContext(CRClient *pClient, GLint ctx) return false; } +static void crServerCleanupMuralCtxUsageCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *mural = (CRMuralInfo *) data1; + CRContext *ctx = (CRContext *) data2; + + CR_STATE_SHAREDOBJ_USAGE_CLEAR(mural, ctx); +} + void SERVER_DISPATCH_APIENTRY crServerDispatchDestroyContext( GLint ctx ) { @@ -198,6 +203,15 @@ crServerDispatchDestroyContext( GLint ctx ) crDebug("CRServer: DestroyContext context %d", ctx); + if (cr_server.currentCtxInfo == crCtxInfo) + { + CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + crServerPerformMakeCurrent(dummyMural, &cr_server.MainContextInfo); + CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo); + } + + crHashtableWalk(cr_server.muralTable, crServerCleanupMuralCtxUsageCB, crCtx); + crCtxInfo->currentMural = NULL; crHashtableDelete(cr_server.contextTable, ctx, NULL); crStateDestroyContext( crCtx ); @@ -268,82 +282,56 @@ crServerDispatchDestroyContext( GLint ctx ) pNode = pNode->next; } - if (cr_server.currentCtxInfo == crCtxInfo) - { - cr_server.currentCtxInfo = &cr_server.MainContextInfo; - } + CRASSERT(cr_server.currentCtxInfo != crCtxInfo); } -void SERVER_DISPATCH_APIENTRY -crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context ) +void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo ) { - CRMuralInfo *mural, *oldMural; - CRContextInfo *ctxInfo = NULL; + CRMuralInfo *oldMural; CRContext *ctx, *oldCtx = NULL; - - if (context >= 0 && window >= 0) { - mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); - if (!mural) - { - crWarning("CRServer: invalid window %d passed to crServerDispatchMakeCurrent()", window); - return; - } - - /* Update the state tracker's current context */ - ctxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, context); - if (!ctxInfo) { - crWarning("CRserver: NULL context in MakeCurrent %d", context); - return; - } - } - else { -#if 0 - oldMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, cr_server.currentWindow); - if (oldMural && oldMural->bUseFBO && crServerSupportRedirMuralFBO()) - { - if (!crStateGetCurrent()->framebufferobject.drawFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); - } - if (!crStateGetCurrent()->framebufferobject.readFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); - } - } - - ctxInfo = &cr_server.MainContextInfo; - window = -1; - mural = NULL; -#endif - cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; - return; - } + GLuint idDrawFBO, idReadFBO; + GLint context = ctxInfo->CreateInfo.externalID; + GLint window = mural->CreateInfo.externalID; cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE; ctx = ctxInfo->pContext; CRASSERT(ctx); - oldMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, cr_server.currentWindow); + oldMural = cr_server.currentMural; /* Ubuntu 11.04 hosts misbehave if context window switch is * done with non-default framebuffer object settings. - * crStateSwichPrepare & crStateSwichPostprocess are supposed to work around this problem - * crStateSwichPrepare restores the FBO state to its default values before the context window switch, - * while crStateSwichPostprocess restores it back to the original values */ - oldCtx = crStateSwichPrepare(ctx, cr_server.bUseMultipleContexts, oldMural && oldMural->bUseFBO && crServerSupportRedirMuralFBO() ? oldMural->idFBO : 0); - - /* - crDebug("**** %s client %d curCtx=%d curWin=%d", __func__, - cr_server.curClient->number, ctxPos, window); - */ - cr_server.curClient->currentContextNumber = context; - cr_server.curClient->currentCtxInfo = ctxInfo; - cr_server.curClient->currentMural = mural; - cr_server.curClient->currentWindow = window; + * crStateSwitchPrepare & crStateSwitchPostprocess are supposed to work around this problem + * crStateSwitchPrepare restores the FBO state to its default values before the context window switch, + * while crStateSwitchPostprocess restores it back to the original values */ + oldCtx = crStateGetCurrent(); + if (oldMural && oldMural->fRedirected && crServerSupportRedirMuralFBO()) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + crStateSwitchPrepare(cr_server.bUseMultipleContexts ? NULL : ctx, oldCtx, idDrawFBO, idReadFBO); - CRASSERT(cr_server.curClient->currentCtxInfo); - CRASSERT(cr_server.curClient->currentCtxInfo->pContext); + if (cr_server.curClient) + { + /* + crDebug("**** %s client %d curCtx=%d curWin=%d", __func__, + cr_server.curClient->number, ctxPos, window); + */ + cr_server.curClient->currentContextNumber = context; + cr_server.curClient->currentCtxInfo = ctxInfo; + cr_server.curClient->currentMural = mural; + cr_server.curClient->currentWindow = window; + + CRASSERT(cr_server.curClient->currentCtxInfo); + CRASSERT(cr_server.curClient->currentCtxInfo->pContext); + } /* This is a hack to force updating the 'current' attribs */ crStateUpdateColorBits(); @@ -380,26 +368,58 @@ crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context ) * used though. */ cr_server.head_spu->dispatch_table.MakeCurrent( mural->spuWindow, - nativeWindow, + 0, ctxInfo->SpuContext >= 0 ? ctxInfo->SpuContext : cr_server.MainContextInfo.SpuContext); + + CR_STATE_SHAREDOBJ_USAGE_SET(mural, ctx); + if (cr_server.currentCtxInfo) + cr_server.currentCtxInfo->currentMural = NULL; + ctxInfo->currentMural = mural; + cr_server.firstCallMakeCurrent = GL_FALSE; cr_server.currentCtxInfo = ctxInfo; cr_server.currentWindow = window; - cr_server.currentNativeWindow = nativeWindow; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = mural; } /* This used to be earlier, after crStateUpdateColorBits() call */ crStateMakeCurrent( ctx ); - crStateSwichPostprocess(oldCtx, cr_server.bUseMultipleContexts, mural->bUseFBO && crServerSupportRedirMuralFBO() ? mural->idFBO : 0); + if (mural && mural->fRedirected && crServerSupportRedirMuralFBO()) + { + GLuint id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + if (id != mural->iCurDrawBuffer) + { + crDebug("DBO draw buffer changed on make current"); + mural->iCurDrawBuffer = id; + } + + id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + if (id != mural->iCurReadBuffer) + { + crDebug("DBO read buffer changed on make current"); + mural->iCurReadBuffer = id; + } + + idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + crStateSwitchPostprocess(ctx, cr_server.bUseMultipleContexts ? NULL : oldCtx, idDrawFBO, idReadFBO); if (!ctx->framebufferobject.drawFB - && (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT)) + && (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT) + && cr_server.curClient) cr_server.curClient->currentMural->bFbDraw = GL_TRUE; - if (!mural->bUseFBO) + if (!mural->fRedirected) { ctx->buffer.width = mural->width; ctx->buffer.height = mural->height; @@ -411,3 +431,51 @@ crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context ) } } + +void SERVER_DISPATCH_APIENTRY +crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context ) +{ + CRMuralInfo *mural; + CRContextInfo *ctxInfo = NULL; + + if (context >= 0 && window >= 0) { + mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) + { + crWarning("CRServer: invalid window %d passed to crServerDispatchMakeCurrent()", window); + return; + } + + /* Update the state tracker's current context */ + ctxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, context); + if (!ctxInfo) { + crWarning("CRserver: NULL context in MakeCurrent %d", context); + return; + } + } + else { +#if 0 + oldMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, cr_server.currentWindow); + if (oldMural && oldMural->bUseFBO && crServerSupportRedirMuralFBO()) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); + } + } + + ctxInfo = &cr_server.MainContextInfo; + window = -1; + mural = NULL; +#endif + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + return; + } + + crServerPerformMakeCurrent( mural, ctxInfo ); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py index cacc3530..9e5b5397 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py @@ -46,6 +46,7 @@ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") for func_name in keys: current = 0 array = "" + condition = "" m = re.search( r"^(Color|Normal)([1234])(ub|b|us|s|ui|i|f|d)$", func_name ) if m : current = 1 @@ -68,6 +69,7 @@ for func_name in keys: name = "texCoord" type = m.group(3) + m.group(2) array = "[texture-GL_TEXTURE0_ARB]" + condition = "if (texture >= GL_TEXTURE0_ARB && texture < GL_TEXTURE0_ARB + CR_MAX_TEXTURE_UNITS)" m = re.match( r"^(Index)(ub|b|us|s|ui|i|f|d)$", func_name ) if m : current = 1 @@ -91,18 +93,23 @@ for func_name in keys: name = string.lower( m.group(1)[:1] ) + m.group(1)[1:] type = m.group(3) + m.group(2) array = "[index]" + condition = "if (index < CR_MAX_VERTEX_ATTRIBS)" if func_name == "VertexAttrib4NubARB": current = 1 name = "vertexAttrib" type = "ub4" array = "[index]" + condition = "if (index < CR_MAX_VERTEX_ATTRIBS)" if current: params = apiutil.Parameters(func_name) print 'void SERVER_DISPATCH_APIENTRY crServerDispatch%s( %s )' % ( func_name, apiutil.MakeDeclarationString(params) ) print '{' - print '\tcr_server.head_spu->dispatch_table.%s( %s );' % (func_name, apiutil.MakeCallString(params) ) - print "\tcr_server.current.c.%s.%s%s = cr_unpackData;" % (name,type,array) + print '\t%s' % (condition) + print '\t{' + print '\t\tcr_server.head_spu->dispatch_table.%s( %s );' % (func_name, apiutil.MakeCallString(params) ) + print "\t\tcr_server.current.c.%s.%s%s = cr_unpackData;" % (name,type,array) + print '\t}' print '}\n' print """ diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py index f1db1f05..9ac9895a 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py @@ -22,6 +22,11 @@ print """ #include "chromium.h" #include "state/cr_statetypes.h" + +#if defined(__cplusplus) +extern "C" { +#endif + """ keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") @@ -36,4 +41,10 @@ for func_name in keys: print '%s SERVER_DISPATCH_APIENTRY crServerDispatch%s( %s );' % (return_type, func_name, apiutil.MakeDeclarationString( params )) -print '#endif /* SERVER_DISPATCH_HEADER */' +print """ +#if defined(__cplusplus) +} +#endif + +#endif /* SERVER_DISPATCH_HEADER */ +""" diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c index cc609377..da25dbb0 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,7 +28,9 @@ crServerDispatchGenFramebuffersEXT(GLsizei n, GLuint *framebuffers) { GLuint *local_buffers = (GLuint *) crAlloc(n * sizeof(*local_buffers)); (void) framebuffers; - cr_server.head_spu->dispatch_table.GenFramebuffersEXT(n, local_buffers); + + crStateGenFramebuffersEXT(n, local_buffers); + crServerReturnValue(local_buffers, n * sizeof(*local_buffers)); crFree(local_buffers); } @@ -38,7 +40,9 @@ crServerDispatchGenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) { GLuint *local_buffers = (GLuint *) crAlloc(n * sizeof(*local_buffers)); (void) renderbuffers; - cr_server.head_spu->dispatch_table.GenFramebuffersEXT(n, local_buffers); + + crStateGenRenderbuffersEXT(n, local_buffers); + crServerReturnValue(local_buffers, n * sizeof(*local_buffers)); crFree(local_buffers); } @@ -71,29 +75,54 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchBindFramebufferEXT(GLenum target, if (0==framebuffer) { CRContext *ctx = crStateGetCurrent(); - if (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT) + if (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT || ctx->buffer.drawBuffer == GL_FRONT_RIGHT) cr_server.curClient->currentMural->bFbDraw = GL_TRUE; } if (0==framebuffer && crServerIsRedirectedToFBO()) { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(target, cr_server.curClient->currentMural->idFBO); + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (target == GL_FRAMEBUFFER) + { + GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + if (idDrawFBO == idReadFBO) + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_FRAMEBUFFER, idDrawFBO); + else + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO); + } + } + else if (target == GL_READ_FRAMEBUFFER) + { + GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO); + } + else if (target == GL_DRAW_FRAMEBUFFER) + { + GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO); + } + else + { + crWarning("unknown target %d", target); + } #ifdef DEBUG_misha - Assert(0); cr_server.head_spu->dispatch_table.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); cr_server.head_spu->dispatch_table.GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); if (GL_FRAMEBUFFER_EXT == target) { - Assert(rfb == cr_server.curClient->currentMural->idFBO); - Assert(dfb == cr_server.curClient->currentMural->idFBO); + Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); } else if (GL_READ_FRAMEBUFFER_EXT == target) { - Assert(rfb == cr_server.curClient->currentMural->idFBO); + Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); } else if (GL_DRAW_FRAMEBUFFER_EXT == target) { - Assert(dfb == cr_server.curClient->currentMural->idFBO); + Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); } else { @@ -163,16 +192,18 @@ crServerDispatchGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum att GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsFramebufferEXT( GLuint framebuffer ) { - GLboolean retval; - retval = cr_server.head_spu->dispatch_table.IsFramebufferEXT(crStateGetFramebufferHWID(framebuffer)); + /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsFramebufferEXT(framebuffer); crServerReturnValue( &retval, sizeof(retval) ); return retval; /* WILL PROBABLY BE IGNORED */ } GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsRenderbufferEXT( GLuint renderbuffer ) { - GLboolean retval; - retval = cr_server.head_spu->dispatch_table.IsRenderbufferEXT(crStateGetRenderbufferHWID(renderbuffer)); + /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsRenderbufferEXT(renderbuffer); crServerReturnValue( &retval, sizeof(retval) ); return retval; /* WILL PROBABLY BE IGNORED */ } diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py index 89391d6c..58d4953e 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py @@ -108,8 +108,6 @@ convert_bufferid = [ 'GetVertexAttribivNV' ]; -from get_components import *; - keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") for func_name in keys: #(return_type, arg_names, arg_types) = gl_mapping[func_name] @@ -142,5 +140,5 @@ for func_name in keys: if func_name in no_pnames: print '\tcrServerReturnValue( &(%s[0]), %d*sizeof(%s) );' % (local_argname, max_components[func_name], local_argtype ); else: - print '\tcrServerReturnValue( &(%s[0]), lookupComponents(pname)*sizeof(%s) );' % (local_argname, local_argtype ); + print '\tcrServerReturnValue( &(%s[0]), crStateHlpComponentsCount(pname)*sizeof(%s) );' % (local_argname, local_argtype ); print '}\n' diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c index 12677f84..20acbc05 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -46,6 +46,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveAttrib(GLuint program, GL zero.length = 0; crServerReturnValue(&zero, sizeof(zero)); } + /* zero out just the header to ensure it initially contains zero size values */ + memset(pLocal, 0, sizeof (*pLocal)); cr_server.head_spu->dispatch_table.GetActiveAttrib(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]); crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t)); crFree(pLocal); @@ -62,6 +64,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveUniform(GLuint program, G zero.length = 0; crServerReturnValue(&zero, sizeof(zero)); } + /* zero out just the header to ensure it initially contains zero size values */ + memset(pLocal, 0, sizeof (*pLocal)); cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]); crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t)); crFree(pLocal); @@ -77,6 +81,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedShaders(GLuint program, GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; cr_server.head_spu->dispatch_table.GetAttachedShaders(crStateGetProgramHWID(program), maxCount, pLocal, (GLuint*)&pLocal[1]); { @@ -91,17 +97,19 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedShaders(GLuint program, crFree(pLocal); } -void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedObjectsARB(GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj) +void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedObjectsARB(VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * count, VBoxGLhandleARB * obj) { GLsizei *pLocal; - pLocal = (GLsizei*) crAlloc(maxCount*sizeof(GLhandleARB)+sizeof(GLsizei)); + pLocal = (GLsizei*) crAlloc(maxCount*sizeof(VBoxGLhandleARB)+sizeof(GLsizei)); if (!pLocal) { GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } - cr_server.head_spu->dispatch_table.GetAttachedObjectsARB(crStateGetProgramHWID(containerObj), maxCount, pLocal, (GLhandleARB*)&pLocal[1]); + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetAttachedObjectsARB(crStateGetProgramHWID(containerObj), maxCount, pLocal, (VBoxGLhandleARB*)&pLocal[1]); { GLsizei i; @@ -111,13 +119,13 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedObjectsARB(GLhandleARB ids[i] = crStateGLSLShaderHWIDtoID(ids[i]); } - crServerReturnValue(pLocal, (*pLocal)*sizeof(GLhandleARB)+sizeof(GLsizei)); + crServerReturnValue(pLocal, (*pLocal)*sizeof(VBoxGLhandleARB)+sizeof(GLsizei)); crFree(pLocal); } AssertCompile(sizeof(GLsizei) == 4); -void SERVER_DISPATCH_APIENTRY crServerDispatchGetInfoLogARB(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog) +void SERVER_DISPATCH_APIENTRY crServerDispatchGetInfoLogARB(VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog) { GLsizei *pLocal; GLuint hwid; @@ -128,6 +136,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetInfoLogARB(GLhandleARB obj, GLs GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; /*@todo: recheck*/ hwid = crStateGetProgramHWID(obj); if (!hwid) hwid = crStateGetShaderHWID(obj); @@ -147,6 +157,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderInfoLog(GLuint shader, GL GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; cr_server.head_spu->dispatch_table.GetShaderInfoLog(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]); crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); crFree(pLocal); @@ -162,6 +174,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramInfoLog(GLuint program, GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; cr_server.head_spu->dispatch_table.GetProgramInfoLog(crStateGetProgramHWID(program), bufSize, pLocal, (char*)&pLocal[1]); CRASSERT(pLocal[0] <= bufSize); crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); @@ -178,6 +192,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderSource(GLuint shader, GLs GLsizei zero=0; crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; cr_server.head_spu->dispatch_table.GetShaderSource(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]); CRASSERT(pLocal[0] <= bufSize); crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); @@ -199,6 +215,8 @@ crServerDispatchGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei crServerReturnValue(&zero, sizeof(zero)); } + /* initial (fallback )value */ + *pLocal = 0; crStateGLSLProgramCacheUniforms(program, maxcbData, pLocal, (char*)&pLocal[1]); crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei)); @@ -207,8 +225,8 @@ crServerDispatchGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei static GLint __GetUniformSize(GLuint program, GLint location) { - GLint size; - GLenum type; + GLint size = 0; + GLenum type = 0; /*@todo: check if index and location is the same*/ cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), location, 0, NULL, &size, &type, NULL); @@ -286,7 +304,7 @@ GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsProgram(GLuint program) return retval; /* ignored */ } -void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterfvARB( GLhandleARB obj, GLenum pname, GLfloat * params ) +void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params ) { GLfloat local_params[1]; GLuint hwid = crStateGetProgramHWID(obj); @@ -305,7 +323,7 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterfvARB( GLhandleA crServerReturnValue( &(local_params[0]), 1*sizeof(GLfloat) ); } -void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterivARB( GLhandleARB obj, GLenum pname, GLint * params ) +void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params ) { GLint local_params[1]; GLuint hwid = crStateGetProgramHWID(obj); diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c index c11d2d00..25d4b91e 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,13 +29,29 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchShaderSource(GLuint shader, GLsizei count, const char ** string, const GLint * length) { /*@todo?crStateShaderSource(shader...);*/ +#ifdef DEBUG_misha + GLenum err = cr_server.head_spu->dispatch_table.GetError(); +#endif cr_server.head_spu->dispatch_table.ShaderSource(crStateGetShaderHWID(shader), count, string, length); +#ifdef DEBUG_misha + err = cr_server.head_spu->dispatch_table.GetError(); + CRASSERT(err == GL_NO_ERROR); +#endif + CR_SERVER_DUMP_SHADER_SOURCE(shader); } void SERVER_DISPATCH_APIENTRY crServerDispatchCompileShader(GLuint shader) { +#ifdef DEBUG_misha + GLint iCompileStatus = GL_FALSE; +#endif crStateCompileShader(shader); cr_server.head_spu->dispatch_table.CompileShader(crStateGetShaderHWID(shader)); +#ifdef DEBUG_misha + cr_server.head_spu->dispatch_table.GetShaderiv(crStateGetShaderHWID(shader), GL_COMPILE_STATUS, &iCompileStatus); + Assert(iCompileStatus == GL_TRUE); +#endif + CR_SERVER_DUMP_COMPILE_SHADER(shader); } void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteShader(GLuint shader) @@ -64,6 +80,7 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchLinkProgram(GLuint program) { crStateLinkProgram(program); cr_server.head_spu->dispatch_table.LinkProgram(crStateGetProgramHWID(program)); + CR_SERVER_DUMP_LINK_PROGRAM(program); } void SERVER_DISPATCH_APIENTRY crServerDispatchUseProgram(GLuint program) @@ -94,23 +111,14 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchBindAttribLocation(GLuint program, cr_server.head_spu->dispatch_table.BindAttribLocation(crStateGetProgramHWID(program), index, name); } -void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteObjectARB(GLhandleARB obj) +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteObjectARB(VBoxGLhandleARB obj) { - GLuint hwid = crStateGetProgramHWID(obj); - - if (!hwid) - { - hwid = crStateGetShaderHWID(obj); - CRASSERT(hwid); - crStateDeleteShader(obj); - } - else - { - crStateDeleteProgram(obj); - } + GLuint hwid = crStateDeleteObjectARB(obj); if (hwid) cr_server.head_spu->dispatch_table.DeleteObjectARB(hwid); + else + crWarning("zero hwid for object %d", obj); } GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetAttribLocation( GLuint program, const char * name ) @@ -121,9 +129,9 @@ GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetAttribLocation( GLuint program return retval; /* WILL PROBABLY BE IGNORED */ } -GLhandleARB SERVER_DISPATCH_APIENTRY crServerDispatchGetHandleARB( GLenum pname ) +VBoxGLhandleARB SERVER_DISPATCH_APIENTRY crServerDispatchGetHandleARB( GLenum pname ) { - GLhandleARB retval; + VBoxGLhandleARB retval; retval = cr_server.head_spu->dispatch_table.GetHandleARB(pname); if (pname==GL_PROGRAM_OBJECT_ARB) { @@ -141,4 +149,19 @@ GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformLocation(GLuint program return retval; /* WILL PROBABLY BE IGNORED */ } +void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramiv( GLuint program, GLenum pname, GLint * params ) +{ + GLint local_params[1]; + (void) params; + cr_server.head_spu->dispatch_table.GetProgramiv(crStateGetProgramHWID(program), pname, local_params); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderiv( GLuint shader, GLenum pname, GLint * params ) +{ + GLint local_params[1]; + (void) params; + cr_server.head_spu->dispatch_table.GetShaderiv( crStateGetShaderHWID(shader), pname, local_params ); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) ); +} #endif /* #ifdef CR_OPENGL_VERSION_2_0 */ diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c index 3079c15b..6c3d9bd0 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c @@ -60,6 +60,7 @@ GLuint crServerTranslateProgramID( GLuint id ) void SERVER_DISPATCH_APIENTRY crServerDispatchNewList( GLuint list, GLenum mode ) { + Assert(0); if (mode == GL_COMPILE_AND_EXECUTE) crWarning("using glNewList(GL_COMPILE_AND_EXECUTE) can confuse the crserver"); @@ -68,10 +69,37 @@ crServerDispatchNewList( GLuint list, GLenum mode ) cr_server.head_spu->dispatch_table.NewList( list, mode ); } +static void crServerQueryHWState() +{ + GLuint fbFbo, bbFbo; + CRClient *client = cr_server.curClient; + CRMuralInfo *mural = client ? client->currentMural : NULL; + if (mural && mural->fRedirected) + { + fbFbo = mural->aidFBOs[CR_SERVER_FBO_FB_IDX(mural)]; + bbFbo = mural->aidFBOs[CR_SERVER_FBO_BB_IDX(mural)]; + } + else + { + fbFbo = bbFbo = 0; + } + crStateQueryHWState(fbFbo, bbFbo); +} + void SERVER_DISPATCH_APIENTRY crServerDispatchEndList(void) { + CRContext *g = crStateGetCurrent(); + CRListsState *l = &(g->lists); + cr_server.head_spu->dispatch_table.EndList(); crStateEndList(); + +#ifndef IN_GUEST + if (l->mode==GL_COMPILE) + { + crServerQueryHWState(); + } +#endif } void SERVER_DISPATCH_APIENTRY @@ -83,7 +111,7 @@ crServerDispatchCallList( GLuint list ) /* we're not compiling, so execute the list now */ /* Issue the list as-is */ cr_server.head_spu->dispatch_table.CallList( list ); - crStateQueryHWState(); + crServerQueryHWState(); } else { /* we're compiling glCallList into another list - just pass it through */ @@ -211,7 +239,7 @@ crServerDispatchCallLists( GLsizei n, GLenum type, const GLvoid *lists ) /* we're not compiling, so execute the list now */ /* Issue the list as-is */ cr_server.head_spu->dispatch_table.CallLists( n, type, lists ); - crStateQueryHWState(); + crServerQueryHWState(); } else { /* we're compiling glCallList into another list - just pass it through */ @@ -264,6 +292,12 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteTextures( GLsizei n, const G newTextures[i] = crStateGetTextureHWID(textures[i]); } + for (i = 0; i < n; ++i) + { + crDebug("DeleteTexture: %d, pid %d, ctx %d", textures[i], (uint32_t)cr_server.curClient->pid, cr_server.currentCtxInfo->pContext->id); + } + + crStateDeleteTextures(n, textures); cr_server.head_spu->dispatch_table.DeleteTextures(n, newTextures); crFree(newTextures); @@ -280,12 +314,13 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchPrioritizeTextures( GLsizei n, con return; } + crStatePrioritizeTextures(n, textures, priorities); + for (i = 0; i < n; i++) { newTextures[i] = crStateGetTextureHWID(textures[i]); } - crStatePrioritizeTextures(n, textures, priorities); cr_server.head_spu->dispatch_table.PrioritizeTextures(n, newTextures, priorities); crFree(newTextures); } diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c index f914569e..1a7e360c 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c @@ -12,7 +12,9 @@ #include "cr_string.h" #include "cr_mem.h" #include "cr_hash.h" +#include "cr_vreg.h" #include "cr_environment.h" +#include "cr_pixeldata.h" #include "server_dispatch.h" #include "state/cr_texture.h" #include "render/renderspu.h" @@ -25,6 +27,7 @@ #endif #include <iprt/assert.h> #include <VBox/err.h> +#include <VBox/log.h> #ifdef VBOXCR_LOGFPS #include <iprt/timer.h> @@ -56,6 +59,8 @@ CRServer cr_server; int tearingdown = 0; /* can't be static */ +static DECLCALLBACK(int) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd); + DECLINLINE(int32_t) crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient) { CRClient *pClient = NULL; @@ -118,11 +123,22 @@ static void deleteContextInfoCallback( void *data ) crFree(c); } +static void deleteMuralInfoCallback( void *data ) +{ + CRMuralInfo *m = (CRMuralInfo *) data; + if (m->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID) /* <- do not do term for default mural as it does not contain any info to be freed, + * and renderspu will destroy it up itself*/ + { + crServerMuralTerm(m); + } + crFree(m); +} static void crServerTearDown( void ) { GLint i; CRClientNode *pNode, *pNext; + GLboolean fOldEnableDiff; /* avoid a race condition */ if (tearingdown) @@ -138,6 +154,17 @@ static void crServerTearDown( void ) crFree( cr_server.overlap_intens ); cr_server.overlap_intens = NULL; + /* needed to make sure window dummy mural not get created on mural destruction + * and generally this should be zeroed up */ + cr_server.currentCtxInfo = NULL; + cr_server.currentWindow = -1; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = NULL; + + /* sync our state with renderspu, + * do it before mural & context deletion to avoid deleting currently set murals/contexts*/ + cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + /* Deallocate all semaphores */ crFreeHashtable(cr_server.semaphores, crFree); cr_server.semaphores = NULL; @@ -149,12 +176,28 @@ static void crServerTearDown( void ) /* Free all context info */ crFreeHashtable(cr_server.contextTable, deleteContextInfoCallback); - /* Free context/window creation info */ - crFreeHashtable(cr_server.pWindowCreateInfoTable, crServerCreateInfoDeleteCB); + /* synchronize with reality */ + fOldEnableDiff = crStateEnableDiffOnMakeCurrent(GL_FALSE); + if(cr_server.MainContextInfo.pContext) + crStateMakeCurrent(cr_server.MainContextInfo.pContext); + crStateEnableDiffOnMakeCurrent(fOldEnableDiff); /* Free vertex programs */ crFreeHashtable(cr_server.programTable, crFree); + /* Free murals */ + crFreeHashtable(cr_server.muralTable, deleteMuralInfoCallback); + + CrPMgrTerm(); + + if (CrBltIsInitialized(&cr_server.Blitter)) + { + CrBltTerm(&cr_server.Blitter); + } + + /* Free dummy murals */ + crFreeHashtable(cr_server.dummyMuralTable, deleteMuralInfoCallback); + for (i = 0; i < cr_server.numClients; i++) { if (cr_server.clients[i]) { CRConnection *conn = cr_server.clients[i]->conn; @@ -174,6 +217,11 @@ static void crServerTearDown( void ) } cr_server.pCleanupClient = NULL; + if (crServerRpwIsInitialized(&cr_server.RpwWorker)) + { + crServerRpwTerm(&cr_server.RpwWorker); + } + #if 1 /* disable these two lines if trying to get stack traces with valgrind */ crSPUUnloadChain(cr_server.head_spu); @@ -183,6 +231,10 @@ static void crServerTearDown( void ) crStateDestroy(); crNetTearDown(); + + VBoxVrListClear(&cr_server.RootVr); + + VBoxVrTerm(); } static void crServerClose( unsigned int id ) @@ -226,8 +278,15 @@ void crServerInit(int argc, char *argv[]) { int i; + const char*env; char *mothership = NULL; CRMuralInfo *defaultMural; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return; + } for (i = 1 ; i < argc ; i++) { @@ -294,6 +353,7 @@ crServerInit(int argc, char *argv[]) */ cr_server.muralTable = crAllocHashtable(); defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; crHashtableAdd(cr_server.muralTable, 0, defaultMural); cr_server.programTable = crAllocHashtable(); @@ -311,9 +371,39 @@ crServerInit(int argc, char *argv[]) cr_server.contextTable = crAllocHashtable(); cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo; + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); + crServerInitDispatch(); + crServerInitTmpCtxDispatch(); crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + crUnpackSetReturnPointer( &(cr_server.return_ptr) ); crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) ); @@ -332,6 +422,13 @@ void crVBoxServerTearDown(void) GLboolean crVBoxServerInit(void) { CRMuralInfo *defaultMural; + const char*env; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return GL_FALSE; + } #if DEBUG_FP_EXCEPTIONS { @@ -367,6 +464,7 @@ GLboolean crVBoxServerInit(void) */ cr_server.muralTable = crAllocHashtable(); defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; crHashtableAdd(cr_server.muralTable, 0, defaultMural); cr_server.programTable = crAllocHashtable(); @@ -385,8 +483,27 @@ GLboolean crVBoxServerInit(void) * Default context */ cr_server.contextTable = crAllocHashtable(); -// cr_server.pContextCreateInfoTable = crAllocHashtable(); - cr_server.pWindowCreateInfoTable = crAllocHashtable(); + + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); crServerSetVBoxConfigurationHGCM(); @@ -394,8 +511,17 @@ GLboolean crVBoxServerInit(void) return GL_FALSE; crServerInitDispatch(); + crServerInitTmpCtxDispatch(); crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + /*Check for PBO support*/ if (crStateGetCurrent()->extensions.ARB_pixel_buffer_object) { @@ -405,6 +531,38 @@ GLboolean crVBoxServerInit(void) return GL_TRUE; } +static int32_t crVBoxServerAddClientObj(uint32_t u32ClientID, CRClient **ppNewClient) +{ + CRClient *newClient; + + if (cr_server.numClients>=CR_MAX_CLIENTS) + { + if (ppNewClient) + *ppNewClient = NULL; + return VERR_MAX_THRDS_REACHED; + } + + newClient = (CRClient *) crCalloc(sizeof(CRClient)); + crDebug("crServer: AddClient u32ClientID=%d", u32ClientID); + + newClient->spu_id = 0; + newClient->currentCtxInfo = &cr_server.MainContextInfo; + newClient->currentContextNumber = -1; + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->conn->u32ClientID = u32ClientID; + + cr_server.clients[cr_server.numClients++] = newClient; + + crServerAddToRunQueue(newClient); + + if (ppNewClient) + *ppNewClient = newClient; + + return VINF_SUCCESS; +} + int32_t crVBoxServerAddClient(uint32_t u32ClientID) { CRClient *newClient; @@ -614,6 +772,23 @@ int32_t crVBoxServerClientRead(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t return crVBoxServerInternalClientRead(pClient, pBuffer, pcbBuffer); } +extern DECLEXPORT(int32_t) crVBoxServerClientGetCaps(uint32_t u32ClientID, uint32_t *pu32Caps) +{ + *pu32Caps = cr_server.u32Caps; + return VINF_SUCCESS; +} + +static int32_t crVBoxServerClientObjSetVersion(CRClient *pClient, uint32_t vMajor, uint32_t vMinor) +{ + pClient->conn->vMajor = vMajor; + pClient->conn->vMinor = vMinor; + + if (vMajor != CR_PROTOCOL_VERSION_MAJOR + || vMinor != CR_PROTOCOL_VERSION_MINOR) + return VERR_NOT_SUPPORTED; + return VINF_SUCCESS; +} + int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint32_t vMinor) { CRClient *pClient=NULL; @@ -630,15 +805,14 @@ int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint } if (!pClient) return VERR_INVALID_PARAMETER; - pClient->conn->vMajor = vMajor; - pClient->conn->vMinor = vMinor; + return crVBoxServerClientObjSetVersion(pClient, vMajor, vMinor); +} - if (vMajor != CR_PROTOCOL_VERSION_MAJOR - || vMinor != CR_PROTOCOL_VERSION_MINOR) - { - return VERR_NOT_SUPPORTED; - } - else return VINF_SUCCESS; +static int32_t crVBoxServerClientObjSetPID(CRClient *pClient, uint64_t pid) +{ + pClient->pid = pid; + + return VINF_SUCCESS; } int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid) @@ -657,9 +831,7 @@ int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid) } if (!pClient) return VERR_INVALID_PARAMETER; - pClient->pid = pid; - - return VINF_SUCCESS; + return crVBoxServerClientObjSetPID(pClient, pid); } int @@ -690,13 +862,16 @@ static void crVBoxServerSaveMuralCB(unsigned long key, void *data1, void *data2) rc = SSMR3PutMem(pSSM, &key, sizeof(key)); CRASSERT(rc == VINF_SUCCESS); - rc = SSMR3PutMem(pSSM, pMI, sizeof(*pMI)); + rc = SSMR3PutMem(pSSM, pMI, RT_OFFSETOF(CRMuralInfo, CreateInfo)); CRASSERT(rc == VINF_SUCCESS); if (pMI->pVisibleRects) { rc = SSMR3PutMem(pSSM, pMI->pVisibleRects, 4*sizeof(GLint)*pMI->cVisibleRects); } + + rc = SSMR3PutMem(pSSM, pMI->ctxUsage, sizeof (pMI->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); } /* @todo add hashtable walker with result info and intermediate abort */ @@ -708,6 +883,9 @@ static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *d CRASSERT(pCreateInfo && pSSM); + /* Don't store default mural create info */ + if (!key) return; + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); CRASSERT(rc == VINF_SUCCESS); @@ -721,10 +899,22 @@ static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *d } } +static void crVBoxServerSaveCreateInfoFromMuralInfoCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMural = (CRMuralInfo *)data1; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pMural->CreateInfo.pszDpyName; + CreateInfo.visualBits = pMural->CreateInfo.requestedVisualBits; + CreateInfo.externalID = pMural->CreateInfo.externalID; + crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); +} + static void crVBoxServerSaveCreateInfoFromCtxInfoCB(unsigned long key, void *data1, void *data2) { CRContextInfo *pContextInfo = (CRContextInfo *)data1; - CRCreateInfo_t CreateInfo = pContextInfo->CreateInfo; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pContextInfo->CreateInfo.pszDpyName; + CreateInfo.visualBits = pContextInfo->CreateInfo.requestedVisualBits; /* saved state contains internal id */ CreateInfo.externalID = pContextInfo->pContext->id; crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); @@ -739,40 +929,672 @@ static void crVBoxServerSyncTextureCB(unsigned long key, void *data1, void *data crStateTextureObjectDiff(pContext, NULL, NULL, pTexture, GL_TRUE); } +typedef struct CRVBOX_SAVE_STATE_GLOBAL +{ + /* context id -> mural association + * on context data save, each context will be made current with the corresponding mural from this table + * thus saving the mural front & back buffer data */ + CRHashTable *contextMuralTable; + /* mural id -> context info + * for murals that do not have associated context in contextMuralTable + * we still need to save*/ + CRHashTable *additionalMuralContextTable; + + PSSMHANDLE pSSM; + + int rc; +} CRVBOX_SAVE_STATE_GLOBAL, *PCRVBOX_SAVE_STATE_GLOBAL; + + +typedef struct CRVBOX_CTXWND_CTXWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + GLuint cAdditionalMurals; +} CRVBOX_CTXWND_CTXWALKER_CB, *PCRVBOX_CTXWND_CTXWALKER_CB; + +static void crVBoxServerBuildAdditionalWindowContextMapCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRContextInfo *pContextInfo = NULL; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + { + Assert(crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + return; + } + + Assert(!crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + + if (cr_server.MainContextInfo.CreateInfo.realVisualBits == pMural->CreateInfo.realVisualBits) + { + pContextInfo = &cr_server.MainContextInfo; + } + else + { + crWarning("different visual bits not implemented!"); + pContextInfo = &cr_server.MainContextInfo; + } + + crHashtableAdd(pData->pGlobal->additionalMuralContextTable, pMural->CreateInfo.externalID, pContextInfo); +} + + +typedef struct CRVBOX_CTXWND_WNDWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + CRContextInfo *pContextInfo; + CRMuralInfo * pMural; +} CRVBOX_CTXWND_WNDWALKER_CB, *PCRVBOX_CTXWND_WNDWALKER_CB; + +static void crVBoxServerBuildContextWindowMapWindowWalkerCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_WNDWALKER_CB pData = (PCRVBOX_CTXWND_WNDWALKER_CB)data2; + + Assert(pData->pMural != pMural); + Assert(pData->pContextInfo); + + if (pData->pMural) + return; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (!CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pData->pContextInfo->pContext)) + return; + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + return; + + CRASSERT(pMural->CreateInfo.realVisualBits == pData->pContextInfo->CreateInfo.realVisualBits); + pData->pMural = pMural; +} + +static void crVBoxServerBuildContextUsedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + + if (!pContextInfo->currentMural) + return; + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pContextInfo->currentMural); + crHashtableAdd(pData->usedMuralTable, pContextInfo->currentMural->CreateInfo.externalID, pContextInfo->currentMural); +} + +CRMuralInfo * crServerGetDummyMural(GLint visualBits) +{ + CRMuralInfo * pMural = (CRMuralInfo *)crHashtableSearch(cr_server.dummyMuralTable, visualBits); + if (!pMural) + { + GLint id; + pMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + if (!pMural) + { + crWarning("crCalloc failed!"); + return NULL; + } + id = crServerMuralInit(pMural, GL_FALSE, visualBits, 0); + if (id < 0) + { + crWarning("crServerMuralInit failed!"); + crFree(pMural); + return NULL; + } + + crHashtableAdd(cr_server.dummyMuralTable, visualBits, pMural); + } + + return pMural; +} + +static void crVBoxServerBuildContextUnusedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRMuralInfo * pMural = NULL; + + if (pContextInfo->currentMural) + return; + + Assert(crHashtableNumElements(pData->pGlobal->contextMuralTable) <= crHashtableNumElements(cr_server.muralTable) - 1); + if (crHashtableNumElements(pData->pGlobal->contextMuralTable) < crHashtableNumElements(cr_server.muralTable) - 1) + { + CRVBOX_CTXWND_WNDWALKER_CB MuralData; + MuralData.pGlobal = pData->pGlobal; + MuralData.usedMuralTable = pData->usedMuralTable; + MuralData.pContextInfo = pContextInfo; + MuralData.pMural = NULL; + + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildContextWindowMapWindowWalkerCB, &MuralData); + + pMural = MuralData.pMural; + + } + + if (!pMural) + { + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + if (!pMural) + { + crWarning("crServerGetDummyMural failed"); + return; + } + } + else + { + crHashtableAdd(pData->usedMuralTable, pMural->CreateInfo.externalID, pMural); + ++pData->cAdditionalMurals; + } + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pMural); +} + +static void crVBoxServerBuildSaveStateGlobal(PCRVBOX_SAVE_STATE_GLOBAL pGlobal) +{ + CRVBOX_CTXWND_CTXWALKER_CB Data; + GLuint cMurals; + pGlobal->contextMuralTable = crAllocHashtable(); + pGlobal->additionalMuralContextTable = crAllocHashtable(); + /* 1. go through all contexts and match all having currentMural set */ + Data.pGlobal = pGlobal; + Data.usedMuralTable = crAllocHashtable(); + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUsedWindowMapCB, &Data); + + cMurals = crHashtableNumElements(pGlobal->contextMuralTable); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + CRASSERT(cMurals == crHashtableNumElements(Data.usedMuralTable)); + if (cMurals < crHashtableNumElements(cr_server.contextTable)) + { + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUnusedWindowMapCB, &Data); + } + + CRASSERT(crHashtableNumElements(pGlobal->contextMuralTable) == crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals + Data.cAdditionalMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + if (cMurals + Data.cAdditionalMurals < crHashtableNumElements(cr_server.muralTable) - 1) + { + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildAdditionalWindowContextMapCB, &Data); + CRASSERT(cMurals + Data.cAdditionalMurals + crHashtableNumElements(pGlobal->additionalMuralContextTable) == crHashtableNumElements(cr_server.muralTable) - 1); + } + + crFreeHashtable(Data.usedMuralTable, NULL); +} + +static void crVBoxServerFBImageDataTerm(CRFBData *pData) +{ + GLuint i; + for (i = 0; i < pData->cElements; ++i) + { + CRFBDataElement * pEl = &pData->aElements[i]; + if (pEl->pvData) + { + crFree(pEl->pvData); + /* sanity */ + pEl->pvData = NULL; + } + } + pData->cElements = 0; +} + +static int crVBoxServerFBImageDataInitEx(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite, uint32_t version, GLuint overrideWidth, GLuint overrideHeight) +{ + CRContext *pContext; + GLuint i; + GLfloat *pF; + CRFBDataElement *pEl; + GLuint width; + GLuint height; + + crMemset(pData, 0, sizeof (*pData)); + + pContext = pCtxInfo->pContext; + + /* the version should be always actual when we do reads, + * i.e. it could differ on writes when snapshot is getting loaded */ + CRASSERT(fWrite || version == SHCROGL_SSM_VERSION); + + width = overrideWidth ? overrideWidth : pMural->width; + height = overrideHeight ? overrideHeight : pMural->height; + + if (!width || !height) + return VINF_SUCCESS; + + if (pMural) + { + if (fWrite) + { + if (!pContext->framebufferobject.drawFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurDrawBuffer); + } + else + { + if (!pContext->framebufferobject.readFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurReadBuffer); + } + } + pData->cElements = 0; + + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_FRONT; + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_RGBA; + pEl->enmType = GL_UNSIGNED_BYTE; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + + /* there is a lot of code that assumes we have double buffering, just assert here to print a warning in the log + * so that we know that something irregular is going on */ + CRASSERT(pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT); + if ((pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT) + || version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + ) + { + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_BB_IDX(pMural)] : 0; + pEl->enmBuffer = pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_BACK; + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_RGBA; + pEl->enmType = GL_UNSIGNED_BYTE; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + + if (version < SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + return VINF_SUCCESS; + + + if (version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL) + { +/* if (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT) */ /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + { + AssertCompile(sizeof (GLfloat) == 4); + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_DEPTH_COMPONENT; + pEl->enmType = GL_FLOAT; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + + /* init to default depth value, just in case */ + pF = (GLfloat*)pEl->pvData; + for (i = 0; i < width * height; ++i) + { + pF[i] = 1.; + } + ++pData->cElements; + } + + /* if (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) */ /* <- older version had a typo which lead to back always being used, + * no matter what the visual bits are */ + { + AssertCompile(sizeof (GLuint) == 4); + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_STENCIL_INDEX; + pEl->enmType = GL_UNSIGNED_INT; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + return VINF_SUCCESS; + } + + if ((pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) + || (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT)) + { + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0; + pEl->enmBuffer = 0; /* we do not care */ + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = GL_DEPTH_STENCIL; + pEl->enmType = GL_UNSIGNED_INT_24_8; + pEl->cbData = width * height * 4; + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning("crVBoxServerFBImageDataInit: crCalloc failed"); + return VERR_NO_MEMORY; + } + ++pData->cElements; + } + return VINF_SUCCESS; +} + +static int crVBoxServerFBImageDataInit(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite) +{ + return crVBoxServerFBImageDataInitEx(pData, pCtxInfo, pMural, fWrite, SHCROGL_SSM_VERSION, 0, 0); +} + +static int crVBoxServerSaveFBImage(PSSMHANDLE pSSM) +{ + CRContextInfo *pCtxInfo; + CRContext *pContext; + CRMuralInfo *pMural; + int32_t rc; + GLuint i; + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4])); + + pCtxInfo = cr_server.currentCtxInfo; + pContext = pCtxInfo->pContext; + pMural = pCtxInfo->currentMural; + + rc = crVBoxServerFBImageDataInit(&Data.data, pCtxInfo, pMural, GL_FALSE); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + + rc = crStateAcquireFBImage(pContext, &Data.data); + AssertRCReturn(rc, rc); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3PutMem(pSSM, pEl->pvData, pEl->cbData); + AssertRCReturn(rc, rc); + } + + crVBoxServerFBImageDataTerm(&Data.data); + + return VINF_SUCCESS; +} + +#define CRSERVER_ASSERTRC_RETURN_VOID(_rc) do { \ + if(!RT_SUCCESS((_rc))) { \ + AssertFailed(); \ + return; \ + } \ + } while (0) + +static void crVBoxServerSaveAdditionalMuralsCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *) data1; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + PSSMHANDLE pSSM = pData->pSSM; + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural = pContextInfo->currentMural; + + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &pContextInfo->CreateInfo.externalID, sizeof(pContextInfo->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + crServerPerformMakeCurrent(pMural, pContextInfo); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); +} + static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2) { CRContextInfo *pContextInfo = (CRContextInfo *) data1; CRContext *pContext = pContextInfo->pContext; - PSSMHANDLE pSSM = (PSSMHANDLE) data2; - int32_t rc; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + PSSMHANDLE pSSM = pData->pSSM; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(pData->contextMuralTable, key); + CRMuralInfo *pContextCurrentMural = pContextInfo->currentMural; + const int32_t i32Dummy = 0; + + AssertCompile(sizeof (i32Dummy) == sizeof (pMural->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); CRASSERT(pContext && pSSM); + CRASSERT(pMural); + CRASSERT(pMural->CreateInfo.externalID); /* We could have skipped saving the key and use similar callback to load context states back, * but there's no guarantee we'd traverse hashtable in same order after loading. */ - rc = SSMR3PutMem(pSSM, &key, sizeof(key)); - CRASSERT(rc == VINF_SUCCESS); + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + +#ifdef DEBUG_misha + { + unsigned long id; + if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id)) + crWarning("No client id for server ctx %d", pContextInfo->CreateInfo.externalID); + else + CRASSERT(id == key); + } +#endif #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - if (cr_server.curClient) + if (pContextInfo->currentMural + || crHashtableSearch(cr_server.muralTable, pMural->CreateInfo.externalID) /* <- this is not a dummy mural */ + ) + { + CRASSERT(pMural->CreateInfo.externalID); + CRASSERT(!crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &pMural->CreateInfo.externalID, sizeof(pMural->CreateInfo.externalID)); + } + else + { + /* this is a dummy mural */ + CRASSERT(!pMural->width); + CRASSERT(!pMural->height); + CRASSERT(crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &i32Dummy, sizeof(pMural->CreateInfo.externalID)); + } + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + CRASSERT(CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pContext)); + CRASSERT(pContextInfo->currentMural == pMural || !pContextInfo->currentMural); + CRASSERT(cr_server.curClient); + + crServerPerformMakeCurrent(pMural, pContextInfo); +#endif + + pData->rc = crStateSaveContext(pContext, pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + /* restore the initial current mural */ + pContextInfo->currentMural = pContextCurrentMural; +} + +#if 0 +typedef struct CR_SERVER_CHECK_BUFFERS +{ + CRBufferObject *obj; + CRContext *ctx; +}CR_SERVER_CHECK_BUFFERS, *PCR_SERVER_CHECK_BUFFERS; + +static void crVBoxServerCheckConsistencyContextBuffersCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo* pContextInfo = (CRContextInfo*)data1; + CRContext *ctx = pContextInfo->pContext; + PCR_SERVER_CHECK_BUFFERS pBuffers = (PCR_SERVER_CHECK_BUFFERS)data2; + CRBufferObject *obj = pBuffers->obj; + CRBufferObjectState *b = &(ctx->bufferobject); + int j, k; + + if (obj == b->arrayBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } + if (obj == b->elementsBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } +#ifdef CR_ARB_pixel_buffer_object + if (obj == b->packBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } + if (obj == b->unpackBuffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } +#endif + +#ifdef CR_ARB_vertex_buffer_object + for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j) { - unsigned long id; - if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id)) + CRClientPointer *cp = crStateGetClientPointerByIndex(j, &ctx->client.array); + if (obj == cp->buffer) { - crWarning("No client id for server ctx %d", pContext->id); + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; } - else + } + + for (k=0; k<ctx->client.vertexArrayStackDepth; ++k) + { + CRVertexArrays *pArray = &ctx->client.vertexArrayStack[k]; + for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j) { - crServerDispatchMakeCurrent(cr_server.curClient->currentWindow, 0, id); + CRClientPointer *cp = crStateGetClientPointerByIndex(j, pArray); + if (obj == cp->buffer) + { + Assert(!pBuffers->ctx || pBuffers->ctx == ctx); + pBuffers->ctx = ctx; + } } } #endif +} - rc = crStateSaveContext(pContext, pSSM); - CRASSERT(rc == VINF_SUCCESS); +static void crVBoxServerCheckConsistencyBuffersCB(unsigned long key, void *data1, void *data2) +{ + CRBufferObject *obj = (CRBufferObject *)data1; + CR_SERVER_CHECK_BUFFERS Buffers = {0}; + Buffers.obj = obj; + crHashtableWalk(cr_server.contextTable, crVBoxServerCheckConsistencyContextBuffersCB, (void*)&Buffers); } +//static void crVBoxServerCheckConsistency2CB(unsigned long key, void *data1, void *data2) +//{ +// CRContextInfo* pContextInfo1 = (CRContextInfo*)data1; +// CRContextInfo* pContextInfo2 = (CRContextInfo*)data2; +// +// CRASSERT(pContextInfo1->pContext); +// CRASSERT(pContextInfo2->pContext); +// +// if (pContextInfo1 == pContextInfo2) +// { +// CRASSERT(pContextInfo1->pContext == pContextInfo2->pContext); +// return; +// } +// +// CRASSERT(pContextInfo1->pContext != pContextInfo2->pContext); +// CRASSERT(pContextInfo1->pContext->shared); +// CRASSERT(pContextInfo2->pContext->shared); +// CRASSERT(pContextInfo1->pContext->shared == pContextInfo2->pContext->shared); +// if (pContextInfo1->pContext->shared != pContextInfo2->pContext->shared) +// return; +// +// crHashtableWalk(pContextInfo1->pContext->shared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, pContextInfo2); +//} +static void crVBoxServerCheckSharedCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo* pContextInfo = (CRContextInfo*)data1; + void **ppShared = (void**)data2; + if (!*ppShared) + *ppShared = pContextInfo->pContext->shared; + else + Assert(pContextInfo->pContext->shared == *ppShared); +} + +static void crVBoxServerCheckConsistency() +{ + CRSharedState *pShared = NULL; + crHashtableWalk(cr_server.contextTable, crVBoxServerCheckSharedCB, (void*)&pShared); + Assert(pShared); + if (pShared) + { + crHashtableWalk(pShared->buffersTable, crVBoxServerCheckConsistencyBuffersCB, NULL); + } +} +#endif + static uint32_t g_hackVBoxServerSaveLoadCallsLeft = 0; DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) @@ -781,8 +1603,18 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) uint32_t ui32; GLboolean b; unsigned long key; + GLenum err; #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - unsigned long ctxID=-1, winID=-1; + CRClient *curClient; + CRMuralInfo *curMural = NULL; + CRContextInfo *curCtxInfo = NULL; +#endif + CRVBOX_SAVE_STATE_GLOBAL Data; + + crMemset(&Data, 0, sizeof (Data)); + +#if 0 + crVBoxServerCheckConsistency(); #endif /* We shouldn't be called if there's no clients at all*/ @@ -811,6 +1643,14 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) return VINF_SUCCESS; } +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_SAVE_START "VBox.Cr.StateSaveStart" +#define CR_DBG_STR_STATE_SAVE_STOP "VBox.Cr.StateSaveStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_START), CR_DBG_STR_STATE_SAVE_START); +#endif + /* Save rendering contexts creation info */ ui32 = crHashtableNumElements(cr_server.contextTable); rc = SSMR3PutU32(pSSM, (uint32_t) ui32); @@ -818,50 +1658,71 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) crHashtableWalk(cr_server.contextTable, crVBoxServerSaveCreateInfoFromCtxInfoCB, pSSM); #ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + curClient = cr_server.curClient; /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */ - if (cr_server.curClient) + if (curClient) { - ctxID = cr_server.curClient->currentContextNumber; - winID = cr_server.curClient->currentWindow; + curCtxInfo = cr_server.curClient->currentCtxInfo; + curMural = cr_server.curClient->currentMural; } -#endif - - /* Save contexts state tracker data */ - /* @todo For now just some blind data dumps, - * but I've a feeling those should be saved/restored in a very strict sequence to - * allow diff_api to work correctly. - * Should be tested more with multiply guest opengl apps working when saving VM snapshot. - */ - crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, pSSM); - -#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE - /* Restore original win and ctx IDs*/ - if (cr_server.curClient) + else if (cr_server.numClients) { - crServerDispatchMakeCurrent(winID, 0, ctxID); + cr_server.curClient = cr_server.clients[0]; } #endif + /* first save windows info */ /* Save windows creation info */ - ui32 = crHashtableNumElements(cr_server.pWindowCreateInfoTable); - rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + ui32 = crHashtableNumElements(cr_server.muralTable); + /* There should be default mural always */ + CRASSERT(ui32>=1); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); AssertRCReturn(rc, rc); - crHashtableWalk(cr_server.pWindowCreateInfoTable, crVBoxServerSaveCreateInfoCB, pSSM); + crHashtableWalk(cr_server.muralTable, crVBoxServerSaveCreateInfoFromMuralInfoCB, pSSM); /* Save cr_server.muralTable * @todo we don't need it all, just geometry info actually */ - ui32 = crHashtableNumElements(cr_server.muralTable); - /* There should be default mural always */ - CRASSERT(ui32>=1); rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); AssertRCReturn(rc, rc); crHashtableWalk(cr_server.muralTable, crVBoxServerSaveMuralCB, pSSM); - /* Save starting free context and window IDs */ - rc = SSMR3PutMem(pSSM, &cr_server.idsPool, sizeof(cr_server.idsPool)); + /* we need to save front & backbuffer data for each mural first create a context -> mural association */ + crVBoxServerBuildSaveStateGlobal(&Data); + + rc = crStateSaveGlobals(pSSM); AssertRCReturn(rc, rc); + Data.pSSM = pSSM; + /* Save contexts state tracker data */ + /* @todo For now just some blind data dumps, + * but I've a feeling those should be saved/restored in a very strict sequence to + * allow diff_api to work correctly. + * Should be tested more with multiply guest opengl apps working when saving VM snapshot. + */ + crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + + ui32 = crHashtableNumElements(Data.additionalMuralContextTable); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + AssertRCReturn(rc, rc); + + crHashtableWalk(Data.additionalMuralContextTable, crVBoxServerSaveAdditionalMuralsCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + cr_server.curClient = curClient; + /* Restore original win and ctx IDs*/ + if (curClient && curMural && curCtxInfo) + { + crServerPerformMakeCurrent(curMural, curCtxInfo); + } + else + { + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + } +#endif + /* Save clients info */ for (i = 0; i < cr_server.numClients; i++) { @@ -881,7 +1742,7 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient)); AssertRCReturn(rc, rc); - if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber>=0) + if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber > 0) { b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtxInfo, &key); CRASSERT(b); @@ -889,7 +1750,7 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) AssertRCReturn(rc, rc); } - if (pClient->currentMural && pClient->currentWindow>=0) + if (pClient->currentMural && pClient->currentWindow > 0) { b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key); CRASSERT(b); @@ -899,8 +1760,21 @@ DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) } } + rc = CrPMgrSaveState(pSSM); + AssertRCReturn(rc, rc); + + /* all context gl error states should have now be synced with chromium erro states, + * reset the error if any */ + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after saving snapshot", err); + cr_server.bIsInSavingState = GL_FALSE; +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_STOP), CR_DBG_STR_STATE_SAVE_STOP); +#endif + return VINF_SUCCESS; } @@ -912,11 +1786,527 @@ static DECLCALLBACK(CRContext*) crVBoxServerGetContextCB(void* pvData) return pContextInfo->pContext; } +typedef struct CR_SERVER_LOADSTATE_READER +{ + PSSMHANDLE pSSM; + uint32_t cbBuffer; + uint32_t cbData; + uint32_t offData; + uint8_t *pu8Buffer; +} CR_SERVER_LOADSTATE_READER; + +static void crServerLsrInit(CR_SERVER_LOADSTATE_READER *pReader, PSSMHANDLE pSSM) +{ + memset(pReader, 0, sizeof (*pReader)); + pReader->pSSM = pSSM; +} + +static void crServerLsrTerm(CR_SERVER_LOADSTATE_READER *pReader) +{ + if (pReader->pu8Buffer) + RTMemFree(pReader->pu8Buffer); + + /* sanity */ + memset(pReader, 0, sizeof (*pReader)); +} + +static int crServerLsrDataGetMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + int rc = VINF_SUCCESS; + uint32_t cbRemaining = cbBuffer; + if (pReader->cbData) + { + uint8_t cbData = RT_MIN(pReader->cbData, cbBuffer); + memcpy(pvBuffer, pReader->pu8Buffer + pReader->offData, cbData); + pReader->cbData -= cbData; + pReader->offData += cbData; + + cbRemaining -= cbData; + pvBuffer = ((uint8_t*)pvBuffer) + cbData; + } + + if (cbRemaining) + { + rc = SSMR3GetMem(pReader->pSSM, pvBuffer, cbRemaining); + AssertRC(rc); + } + + return rc; +} + +static int crServerLsrDataGetU32(CR_SERVER_LOADSTATE_READER *pReader, uint32_t *pu32) +{ + return crServerLsrDataGetMem(pReader, pu32, sizeof (*pu32)); +} + +static int crServerLsrDataPutMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + if (!pReader->cbData && pReader->cbBuffer >= cbBuffer) + { + pReader->offData = 0; + pReader->cbData = cbBuffer; + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + } + else if (pReader->offData >= cbBuffer) + { + pReader->offData -= cbBuffer; + pReader->cbData += cbBuffer; + memcpy(pReader->pu8Buffer + pReader->offData, pvBuffer, cbBuffer); + } + else + { + uint8_t *pu8Buffer = pReader->pu8Buffer; + + pReader->pu8Buffer = (uint8_t*)RTMemAlloc(cbBuffer + pReader->cbData); + if (!pReader->pu8Buffer) + { + crWarning("failed to allocate mem %d", cbBuffer + pReader->cbData); + return VERR_NO_MEMORY; + } + + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + if (pu8Buffer) + { + memcpy(pReader->pu8Buffer + cbBuffer, pu8Buffer + pReader->offData, pReader->cbData); + RTMemFree(pu8Buffer); + } + else + { + Assert(!pReader->cbData); + } + pReader->offData = 0; + pReader->cbData += cbBuffer; + } + + return VINF_SUCCESS; +} + +/* data to be skipped */ + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_2 +{ + void*ListHead_pNext; + void*ListHead_pPrev; + uint32_t cEntries; +} CR_SERVER_BUGGY_MURAL_DATA_2; +typedef struct CR_SERVER_BUGGY_MURAL_DATA_1 +{ + /* VBOXVR_COMPOSITOR_ENTRY Ce; */ + void*Ce_Node_pNext; + void*Ce_Node_pPrev; + CR_SERVER_BUGGY_MURAL_DATA_2 Vr; + /* VBOXVR_TEXTURE Tex; */ + uint32_t Tex_width; + uint32_t Tex_height; + uint32_t Tex_target; + uint32_t Tex_hwid; + /* RTPOINT Pos; */ + uint32_t Pos_x; + uint32_t Pos_y; + uint32_t fChanged; + uint32_t cRects; + void* paSrcRects; + void* paDstRects; +} CR_SERVER_BUGGY_MURAL_DATA_1; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_4 +{ + uint32_t u32Magic; + int32_t cLockers; + RTNATIVETHREAD NativeThreadOwner; + int32_t cNestings; + uint32_t fFlags; + void* EventSem; + R3R0PTRTYPE(PRTLOCKVALRECEXCL) pValidatorRec; + RTHCPTR Alignment; +} CR_SERVER_BUGGY_MURAL_DATA_4; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_3 +{ + void*Compositor_List_pNext; + void*Compositor_List_pPrev; + void*Compositor_pfnEntryRemoved; + float StretchX; + float StretchY; + uint32_t cRects; + uint32_t cRectsBuffer; + void*paSrcRects; + void*paDstRects; + CR_SERVER_BUGGY_MURAL_DATA_4 CritSect; +} CR_SERVER_BUGGY_MURAL_DATA_3; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA +{ + uint8_t fRootVrOn; + CR_SERVER_BUGGY_MURAL_DATA_1 RootVrCEntry; + CR_SERVER_BUGGY_MURAL_DATA_3 RootVrCompositor; +} CR_SERVER_BUGGY_MURAL_DATA; + +AssertCompile(sizeof (CR_SERVER_BUGGY_MURAL_DATA) < sizeof (CRClient)); + +static int32_t crVBoxServerLoadMurals(CR_SERVER_LOADSTATE_READER *pReader, uint32_t version) +{ + unsigned long key; + uint32_t ui, uiNumElems; + bool fBuggyMuralData = false; + /* Load windows */ + int32_t rc = crServerLsrDataGetU32(pReader, &uiNumElems); + AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRCreateInfo_t createInfo; + char psz[200]; + GLint winID; + unsigned long key; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &createInfo, sizeof(createInfo)); + AssertRCReturn(rc, rc); + + CRASSERT(!pReader->cbData); + + if (createInfo.pszDpyName) + { + rc = SSMR3GetStrZEx(pReader->pSSM, psz, 200, NULL); + AssertRCReturn(rc, rc); + createInfo.pszDpyName = psz; + } + + winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key); + CRASSERT((int64_t)winID == (int64_t)key); + } + + /* Load cr_server.muralTable */ + rc = SSMR3GetU32(pReader->pSSM, &uiNumElems); + AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRMuralInfo muralInfo; + CRMuralInfo *pActualMural = NULL; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &muralInfo, RT_OFFSETOF(CRMuralInfo, CreateInfo)); + AssertRCReturn(rc, rc); + + if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING) + muralInfo.bFbDraw = GL_TRUE; + + if (!ui && version == SHCROGL_SSM_VERSION_WITH_BUGGY_MURAL_INFO) + { + /* Lookahead buffer used to determine whether the data erroneously stored root visible regions data */ + union + { + void * apv[1]; + CR_SERVER_BUGGY_MURAL_DATA Data; + /* need to chak spuWindow, so taking the offset of filed following it*/ + uint8_t au8[RT_OFFSETOF(CRMuralInfo, screenId)]; + RTRECT aVisRects[sizeof (CR_SERVER_BUGGY_MURAL_DATA) / sizeof (RTRECT)]; + } LaBuf; + + do { + /* first value is bool (uint8_t) value followed by pointer-size-based alignment. + * the mural memory is zero-initialized initially, so we can be sure the padding is zeroed */ + rc = crServerLsrDataGetMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertRCReturn(rc, rc); + if (LaBuf.apv[0] != NULL && LaBuf.apv[0] != ((void*)1)) + break; + + /* check that the pointers are either valid or NULL */ + if(LaBuf.Data.RootVrCEntry.Ce_Node_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev)) + break; + + /* the entry can can be the only one within the (mural) compositor, + * so its compositor entry node can either contain NULL pNext and pPrev, + * or both of them pointing to compositor's list head */ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext != LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* can either both or none be NULL */ + if (!LaBuf.Data.RootVrCEntry.Ce_Node_pNext != !LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + if (!LaBuf.Data.fRootVrOn) + { + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext || LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* either non-initialized (zeroed) or empty list */ + if (LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext != LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + break; + } + else + { + /* the entry should be initialized */ + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext) + break; + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + { + /* entry should be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev == NULL) + break; + CRASSERT(LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + else + { + /* entry should NOT be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev != NULL) + break; + CRASSERT(!LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + } + +#if 0 + if (muralInfo.pVisibleRects) + { + int j; + int cRects = RT_MIN(muralInfo.cVisibleRects, RT_ELEMENTS(LaBuf.aVisRects)); + CRASSERT(cRects); + for (j = 0; j < cRects; ++j) + { + PRTRECT pRect = &LaBuf.aVisRects[j]; + if (pRect->xLeft >= pRect->xRight) + break; + if (pRect->yTop >= pRect->yBottom) + break; + if (pRect->xLeft < 0 || pRect->xRight < 0 + || pRect->yTop < 0 || pRect->yBottom < 0) + break; + if (pRect->xLeft > (GLint)muralInfo.width + || pRect->xRight > (GLint)muralInfo.width) + break; + if (pRect->yTop > (GLint)muralInfo.height + || pRect->yBottom > (GLint)muralInfo.height) + break; + } + + if (j < cRects) + { + fBuggyMuralData = true; + break; + } + } + + if (muralInfo.pVisibleRects) + { + /* @todo: do we actually need any further checks here? */ + fBuggyMuralData = true; + break; + } + + /* no visible regions*/ + + if (ui == uiNumElems - 1) + { + /* this is the last mural, next it goes idsPool, whose content can not match the above template again */ + fBuggyMuralData = true; + break; + } + + /* next it goes a next mural info */ +// if (!fExpectPtr) +// { +// CRMuralInfo *pNextSpuWindowInfoMural = (CRMuralInfo*)((void*)&LaBuf); +// if (!pNextSpuWindowInfoMural->spuWindow) +// fBuggyMuralData = true; +// +// break; +// } +#endif + /* fExpectPtr == true, the valid pointer values should not match possible mural width/height/position */ + fBuggyMuralData = true; + break; + + } while (0); + + rc = crServerLsrDataPutMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertRCReturn(rc, rc); + } + + if (fBuggyMuralData) + { + CR_SERVER_BUGGY_MURAL_DATA Tmp; + rc = crServerLsrDataGetMem(pReader, &Tmp, sizeof (Tmp)); + AssertRCReturn(rc, rc); + } + + if (muralInfo.pVisibleRects) + { + muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); + if (!muralInfo.pVisibleRects) + { + return VERR_NO_MEMORY; + } + + rc = crServerLsrDataGetMem(pReader, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); + AssertRCReturn(rc, rc); + } + + pActualMural = (CRMuralInfo *)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pActualMural); + + if (version >= SHCROGL_SSM_VERSION_WITH_WINDOW_CTX_USAGE) + { + rc = crServerLsrDataGetMem(pReader, pActualMural->ctxUsage, sizeof (pActualMural->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); + } + + /* Restore windows geometry info */ + crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height); + crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY); + /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/ + if (muralInfo.bReceivedRects) + { + crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects); + } + crServerDispatchWindowShow(key, muralInfo.bVisible); + + if (muralInfo.pVisibleRects) + { + crFree(muralInfo.pVisibleRects); + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + +static int crVBoxServerLoadFBImage(PSSMHANDLE pSSM, uint32_t version, + CRContextInfo* pContextInfo, CRMuralInfo *pMural) +{ + CRContext *pContext = pContextInfo->pContext; + int32_t rc = VINF_SUCCESS; + GLuint i; + /* can apply the data right away */ + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_OFFSETOF(CRFBData, aElements[4])); + + if (version >= SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + { + if (!pMural->width || !pMural->height) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, 0, 0); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + else + { + GLint storedWidth, storedHeight; + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(cr_server.currentCtxInfo == pContextInfo); + CRASSERT(cr_server.currentMural == pMural); + storedWidth = pMural->width; + storedHeight = pMural->height; + } + else + { + storedWidth = pContext->buffer.storedWidth; + storedHeight = pContext->buffer.storedHeight; + } + + if (!storedWidth || !storedHeight) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, storedWidth, storedHeight); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + + CRASSERT(Data.data.cElements); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3GetMem(pSSM, pEl->pvData, pEl->cbData); + AssertRCReturn(rc, rc); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRBufferState *pBuf = &pContext->buffer; + /* can apply the data right away */ + CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo); + CRASSERT(cr_server.currentMural); + + cr_server.head_spu->dispatch_table.MakeCurrent( pMural->spuWindow, + 0, + pContextInfo->SpuContext >= 0 + ? pContextInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + crStateApplyFBImage(pContext, &Data.data); + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + crVBoxServerFBImageDataTerm(&Data.data); + + crServerPresentFBO(pMural); + + CRASSERT(cr_server.currentMural); + cr_server.head_spu->dispatch_table.MakeCurrent( cr_server.currentMural->spuWindow, + 0, + cr_server.currentCtxInfo->SpuContext >= 0 + ? cr_server.currentCtxInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + } + else + { + CRBufferState *pBuf = &pContext->buffer; + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + CRASSERT(Data.data.cElements); /* <- older versions always saved front and back, and we filtered out the null-sized buffers above */ + + if (Data.data.cElements) + { + CRFBData *pLazyData = crAlloc(RT_OFFSETOF(CRFBData, aElements[Data.data.cElements])); + if (!RT_SUCCESS(rc)) + { + crVBoxServerFBImageDataTerm(&Data.data); + crWarning("crAlloc failed"); + return VERR_NO_MEMORY; + } + + crMemcpy(pLazyData, &Data.data, RT_OFFSETOF(CRFBData, aElements[Data.data.cElements])); + pBuf->pFrontImg = pLazyData; + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) { int32_t rc, i; uint32_t ui, uiNumElems; unsigned long key; + GLenum err; + CR_SERVER_LOADSTATE_READER Reader; if (!cr_server.bIsInLoadingState) { @@ -941,6 +2331,16 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; } + crServerLsrInit(&Reader, pSSM); + +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_LOAD_START "VBox.Cr.StateLoadStart" +#define CR_DBG_STR_STATE_LOAD_STOP "VBox.Cr.StateLoadStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_START), CR_DBG_STR_STATE_LOAD_START); +#endif + /* Load and recreate rendering contexts */ rc = SSMR3GetU32(pSSM, &uiNumElems); AssertRCReturn(rc, rc); @@ -974,11 +2374,44 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) pContext->shared->id=-1; } + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(!Reader.pu8Buffer); + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertRCReturn(rc, rc); + CRASSERT(!Reader.pu8Buffer); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA && uiNumElems) + { + /* set the current client to allow doing crServerPerformMakeCurrent later */ + CRASSERT(cr_server.numClients); + cr_server.curClient = cr_server.clients[0]; + } + + rc = crStateLoadGlobals(pSSM, version); + AssertRCReturn(rc, rc); + + if (uiNumElems) + { + /* ensure we have main context set up as current */ + CRMuralInfo *pMural; + CRASSERT(cr_server.MainContextInfo.SpuContext > 0); + CRASSERT(!cr_server.currentCtxInfo); + CRASSERT(!cr_server.currentMural); + pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CRASSERT(pMural); + crServerPerformMakeCurrent(pMural, &cr_server.MainContextInfo); + } + /* Restore context state data */ for (ui=0; ui<uiNumElems; ++ui) { CRContextInfo* pContextInfo; CRContext *pContext; + CRMuralInfo *pMural = NULL; + int32_t winId = 0; rc = SSMR3GetMem(pSSM, &key, sizeof(key)); AssertRCReturn(rc, rc); @@ -988,82 +2421,91 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) CRASSERT(pContextInfo->pContext); pContext = pContextInfo->pContext; - rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version); - AssertRCReturn(rc, rc); - } - - /* Load windows */ - rc = SSMR3GetU32(pSSM, &uiNumElems); - AssertRCReturn(rc, rc); - for (ui=0; ui<uiNumElems; ++ui) - { - CRCreateInfo_t createInfo; - char psz[200]; - GLint winID; - unsigned long key; - - rc = SSMR3GetMem(pSSM, &key, sizeof(key)); - AssertRCReturn(rc, rc); - rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo)); - AssertRCReturn(rc, rc); - - if (createInfo.pszDpyName) + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) { - rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL); + rc = SSMR3GetMem(pSSM, &winId, sizeof(winId)); AssertRCReturn(rc, rc); - createInfo.pszDpyName = psz; + + if (winId) + { + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, winId); + CRASSERT(pMural); + } + else + { + /* null winId means a dummy mural, get it */ + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + CRASSERT(pMural); + } } - winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key); - CRASSERT((int64_t)winID == (int64_t)key); + rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version); + AssertRCReturn(rc, rc); + + /*Restore front/back buffer images*/ + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); + AssertRCReturn(rc, rc); } - /* Load cr_server.muralTable */ - rc = SSMR3GetU32(pSSM, &uiNumElems); - AssertRCReturn(rc, rc); - for (ui=0; ui<uiNumElems; ++ui) + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) { - CRMuralInfo muralInfo; + CRContextInfo *pContextInfo; + CRMuralInfo *pMural; + GLint ctxId; - rc = SSMR3GetMem(pSSM, &key, sizeof(key)); - AssertRCReturn(rc, rc); - rc = SSMR3GetMem(pSSM, &muralInfo, sizeof(muralInfo)); + rc = SSMR3GetU32(pSSM, &uiNumElems); AssertRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural; - if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING) - muralInfo.bFbDraw = GL_TRUE; + rc = SSMR3GetMem(pSSM, &key, sizeof(key)); + AssertRCReturn(rc, rc); - if (muralInfo.pVisibleRects) - { - muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); - if (!muralInfo.pVisibleRects) + rc = SSMR3GetMem(pSSM, &ctxId, sizeof(ctxId)); + AssertRCReturn(rc, rc); + + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pMural); + if (ctxId) { - return VERR_NO_MEMORY; + pContextInfo = (CRContextInfo *)crHashtableSearch(cr_server.contextTable, ctxId); + CRASSERT(pContextInfo); } + else + pContextInfo = &cr_server.MainContextInfo; - rc = SSMR3GetMem(pSSM, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + pInitialCurMural = pContextInfo->currentMural; + + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); AssertRCReturn(rc, rc); - } - /* Restore windows geometry info */ - crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height); - crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY); - /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/ - if (muralInfo.bReceivedRects) - { - crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects); + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; } - crServerDispatchWindowShow(key, muralInfo.bVisible); - if (muralInfo.pVisibleRects) - { - crFree(muralInfo.pVisibleRects); - } + CRASSERT(!uiNumElems || cr_server.currentCtxInfo == &cr_server.MainContextInfo); + + cr_server.curClient = NULL; + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; } + else + { + CRServerFreeIDsPool_t dummyIdsPool; - /* Load starting free context and window IDs */ - rc = SSMR3GetMem(pSSM, &cr_server.idsPool, sizeof(cr_server.idsPool)); - CRASSERT(rc == VINF_SUCCESS); + CRASSERT(!Reader.pu8Buffer); + + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertRCReturn(rc, rc); + + /* not used any more, just read it out and ignore */ + rc = crServerLsrDataGetMem(&Reader, &dummyIdsPool, sizeof(dummyIdsPool)); + CRASSERT(rc == VINF_SUCCESS); + } /* Load clients info */ for (i = 0; i < cr_server.numClients; i++) @@ -1074,21 +2516,21 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) CRClient client; unsigned long ctxID=-1, winID=-1; - rc = SSMR3GetU32(pSSM, &ui); + rc = crServerLsrDataGetU32(&Reader, &ui); AssertRCReturn(rc, rc); /* If this assert fires, then we should search correct client in the list first*/ CRASSERT(ui == pClient->conn->u32ClientID); if (version>=4) { - rc = SSMR3GetU32(pSSM, &pClient->conn->vMajor); + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMajor); AssertRCReturn(rc, rc); - rc = SSMR3GetU32(pSSM, &pClient->conn->vMinor); + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMinor); AssertRCReturn(rc, rc); } - rc = SSMR3GetMem(pSSM, &client, sizeof(client)); + rc = crServerLsrDataGetMem(&Reader, &client, sizeof(client)); CRASSERT(rc == VINF_SUCCESS); client.conn = pClient->conn; @@ -1105,9 +2547,9 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) cr_server.curClient = pClient; - if (client.currentCtxInfo && client.currentContextNumber>=0) + if (client.currentCtxInfo && client.currentContextNumber > 0) { - rc = SSMR3GetMem(pSSM, &ctxID, sizeof(ctxID)); + rc = crServerLsrDataGetMem(&Reader, &ctxID, sizeof(ctxID)); AssertRCReturn(rc, rc); client.currentCtxInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, ctxID); CRASSERT(client.currentCtxInfo); @@ -1116,9 +2558,9 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) //pClient->currentContextNumber = ctxID; } - if (client.currentMural && client.currentWindow>=0) + if (client.currentMural && client.currentWindow > 0) { - rc = SSMR3GetMem(pSSM, &winID, sizeof(winID)); + rc = crServerLsrDataGetMem(&Reader, &winID, sizeof(winID)); AssertRCReturn(rc, rc); client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID); CRASSERT(client.currentMural); @@ -1126,6 +2568,8 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) //pClient->currentWindow = winID; } + CRASSERT(!Reader.cbData); + /* Restore client active context and window */ crServerDispatchMakeCurrent(winID, 0, ctxID); @@ -1195,40 +2639,68 @@ DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) cr_server.curClient = NULL; + if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_INFO) { - GLenum err = crServerDispatchGetError(); - - if (err != GL_NO_ERROR) - { - crWarning("crServer: glGetError %d after loading snapshot", err); - } + rc = CrPMgrLoadState(pSSM, version); + AssertRCReturn(rc, rc); } + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after loading snapshot", err); + cr_server.bIsInLoadingState = GL_FALSE; +#if 0 + crVBoxServerCheckConsistency(); +#endif + +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_STOP), CR_DBG_STR_STATE_LOAD_STOP); +#endif + + CRASSERT(!Reader.cbData); + crServerLsrTerm(&Reader); + return VINF_SUCCESS; } #define SCREEN(i) (cr_server.screen[i]) #define MAPPED(screen) ((screen).winID != 0) -static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2) +extern DECLEXPORT(void) crServerVBoxSetNotifyEventCB(PFNCRSERVERNOTIFYEVENT pfnCb) { - CRMuralInfo *pMI = (CRMuralInfo*) data1; - int *sIndex = (int*) data2; + cr_server.pfnNotifyEventCB = pfnCb; +} - if (pMI->screenId == *sIndex) +void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void*pvData) +{ + /* this is something unexpected, but just in case */ + if (idScreen >= cr_server.screenCount) { - renderspuReparentWindow(pMI->spuWindow); + crWarning("invalid screen id %d", idScreen); + return; } + + cr_server.pfnNotifyEventCB(idScreen, uEvent, pvData); } -static void crVBoxServerCheckMuralCB(unsigned long key, void *data1, void *data2) +void crServerWindowReparent(CRMuralInfo *pMural) +{ + pMural->fHasParentWindow = !!cr_server.screen[pMural->screenId].winID; + + renderspuReparentWindow(pMural->spuWindow); +} + +static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2) { CRMuralInfo *pMI = (CRMuralInfo*) data1; - (void) data2; + int *sIndex = (int*) data2; - crServerCheckMuralGeometry(pMI); + if (pMI->screenId == *sIndex) + { + crServerWindowReparent(pMI); + } } DECLEXPORT(int32_t) crVBoxServerSetScreenCount(int sCount) @@ -1269,9 +2741,14 @@ DECLEXPORT(int32_t) crVBoxServerUnmapScreen(int sIndex) renderspuSetWindowId(0); crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); + + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + + CrPMgrScreenChanged((uint32_t)sIndex); } renderspuSetWindowId(SCREEN(0).winID); + return VINF_SUCCESS; } @@ -1296,9 +2773,9 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint renderspuSetWindowId(SCREEN(sIndex).winID); crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); - renderspuSetWindowId(SCREEN(0).winID); - crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL); + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + renderspuSetWindowId(SCREEN(0).winID); #ifndef WINDOWS /*Restore FB content for clients, which have current window on a screen being remapped*/ @@ -1310,7 +2787,7 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint cr_server.curClient = cr_server.clients[i]; if (cr_server.curClient->currentCtxInfo && cr_server.curClient->currentCtxInfo->pContext - && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg || cr_server.curClient->currentCtxInfo->pContext->buffer.pBackImg) + && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg) && cr_server.curClient->currentMural && cr_server.curClient->currentMural->screenId == sIndex && cr_server.curClient->currentCtxInfo->pContext->buffer.storedHeight == h @@ -1318,25 +2795,74 @@ DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint { int clientWindow = cr_server.curClient->currentWindow; int clientContext = cr_server.curClient->currentContextNumber; + CRFBData *pLazyData = (CRFBData *)cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg; if (clientWindow && clientWindow != cr_server.currentWindow) { crServerDispatchMakeCurrent(clientWindow, 0, clientContext); } - crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext); + crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext, pLazyData); + crStateFreeFBImageLegacy(cr_server.curClient->currentCtxInfo->pContext); } } cr_server.curClient = NULL; } #endif + CrPMgrScreenChanged((uint32_t)sIndex); + return VINF_SUCCESS; } -DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, GLint *pRects) +DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, const RTRECT *pRects) { - renderspuSetRootVisibleRegion(cRects, pRects); + int32_t rc = VINF_SUCCESS; + GLboolean fOldRootVrOn = cr_server.fRootVrOn; + + /* non-zero rects pointer indicate rects are present and switched on + * i.e. cRects==0 and pRects!=NULL means root visible regioning is ON and there are no visible regions, + * while pRects==NULL means root visible regioning is OFF, i.e. everything is visible */ + if (pRects) + { + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + rc = VBoxVrListRectsSet(&cr_server.RootVr, cRects, pRects, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrListRectsSet failed! rc %d", rc); + return rc; + } + + cr_server.fRootVrOn = GL_TRUE; + } + else + { + if (!cr_server.fRootVrOn) + return VINF_SUCCESS; + + VBoxVrListClear(&cr_server.RootVr); + + cr_server.fRootVrOn = GL_FALSE; + } + + if (!fOldRootVrOn != !cr_server.fRootVrOn) + { + rc = CrPMgrModeRootVr(cr_server.fRootVrOn); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrModeRootVr failed rc %d", rc); + return rc; + } + } + else if (cr_server.fRootVrOn) + { + rc = CrPMgrRootVrUpdate(); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrRootVrUpdate failed rc %d", rc); + return rc; + } + } return VINF_SUCCESS; } @@ -1348,21 +2874,7 @@ DECLEXPORT(void) crVBoxServerSetPresentFBOCB(PFNCRSERVERPRESENTFBO pfnPresentFBO DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value) { - if (cr_server.bForceOffscreenRendering==value) - { - return VINF_SUCCESS; - } - - if (value && !crServerSupportRedirMuralFBO()) - { - return VERR_NOT_SUPPORTED; - } - - cr_server.bForceOffscreenRendering=value; - - crHashtableWalk(cr_server.muralTable, crVBoxServerCheckMuralCB, NULL); - - return VINF_SUCCESS; + return CrPMgrModeVrdp(value); } DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks) @@ -1371,74 +2883,502 @@ DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallb if (pCallbacks) { cr_server.outputRedirect = *pCallbacks; - cr_server.bUseOutputRedirect = true; } else { - cr_server.bUseOutputRedirect = false; + memset (&cr_server.outputRedirect, 0, sizeof (cr_server.outputRedirect)); } - // @todo dynamically intercept already existing output: - // crHashtableWalk(cr_server.muralTable, crVBoxServerOutputRedirectCB, NULL); + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h) +{ + CRScreenViewportInfo *pViewport; + RTRECT NewRect; + int rc; + + crDebug("crVBoxServerSetScreenViewport(%i)", sIndex); + + if (sIndex<0 || sIndex>=cr_server.screenCount) + { + crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex); + return VERR_INVALID_PARAMETER; + } + + NewRect.xLeft = x; + NewRect.yTop = y; + NewRect.xRight = x + w; + NewRect.yBottom = y + h; + + pViewport = &cr_server.screenVieport[sIndex]; + /*always do viewport updates no matter whether the rectangle actually changes, + * this is needed to ensure window is adjusted properly on OSX */ + pViewport->Rect = NewRect; + rc = CrPMgrViewportUpdate((uint32_t)sIndex); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrViewportUpdate failed %d", rc); + return rc; + } return VINF_SUCCESS; } -static void crVBoxServerUpdateScreenViewportCB(unsigned long key, void *data1, void *data2) +static void crVBoxServerDefaultContextSet() { - CRMuralInfo *mural = (CRMuralInfo*) data1; - int *sIndex = (int*) data2; + GLint spuWindow, spuCtx; - if (mural->screenId != *sIndex) - return; + if (cr_server.MainContextInfo.SpuContext) + { + CRMuralInfo *pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pMural) + { + WARN(("dummy mural is NULL")); + spuCtx = CR_RENDER_DEFAULT_CONTEXT_ID; + spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + } + else + { + spuCtx = cr_server.MainContextInfo.SpuContext; + spuWindow = pMural->CreateInfo.realVisualBits; + } + } + else + { + spuCtx = CR_RENDER_DEFAULT_CONTEXT_ID; + spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + } - crServerCheckMuralGeometry(mural); + cr_server.head_spu->dispatch_table.MakeCurrent(spuWindow, 0, spuCtx); } +#ifdef VBOX_WITH_CRHGSMI -DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h) +static int32_t crVBoxServerCmdVbvaCrCmdProcess(struct VBOXCMDVBVA_CRCMD_CMD *pCmd, uint32_t cbCmd) { - CRScreenViewportInfo *pVieport; - GLboolean fPosChanged, fSizeChanged; + int32_t rc; + uint32_t cBuffers = pCmd->cBuffers; + uint32_t cParams; + uint32_t cbHdr; + CRVBOXHGSMIHDR *pHdr; + uint32_t u32Function; + uint32_t u32ClientID; + CRClient *pClient; - crDebug("crVBoxServerSetScreenViewport(%i)", sIndex); + if (!g_pvVRamBase) + { + WARN(("g_pvVRamBase is not initialized")); + return VERR_INVALID_STATE; + } - if (sIndex<0 || sIndex>=cr_server.screenCount) + if (!cBuffers) { - crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex); + WARN(("zero buffers passed in!")); return VERR_INVALID_PARAMETER; } - pVieport = &cr_server.screenVieport[sIndex]; - fPosChanged = (pVieport->x != x || pVieport->y != y); - fSizeChanged = (pVieport->w != w || pVieport->h != h); + cParams = cBuffers-1; - if (!fPosChanged && !fSizeChanged) + if (cbCmd != RT_OFFSETOF(VBOXCMDVBVA_CRCMD_CMD, aBuffers[cBuffers])) { - crDebug("crVBoxServerSetScreenViewport: no changes"); - return VINF_SUCCESS; + WARN(("invalid buffer size")); + return VERR_INVALID_PARAMETER; } - if (fPosChanged) + cbHdr = pCmd->aBuffers[0].cbBuffer; + pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); + if (!pHdr) { - pVieport->x = x; - pVieport->y = y; + WARN(("invalid header buffer!")); + return VERR_INVALID_PARAMETER; + } - crHashtableWalk(cr_server.muralTable, crVBoxServerUpdateScreenViewportCB, &sIndex); + if (cbHdr < sizeof (*pHdr)) + { + WARN(("invalid header buffer size!")); + return VERR_INVALID_PARAMETER; } - if (fSizeChanged) + u32Function = pHdr->u32Function; + u32ClientID = pHdr->u32ClientID; + + switch (u32Function) { - pVieport->w = w; - pVieport->h = h; + case SHCRGL_GUEST_FN_WRITE: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + else + { + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + break; + } + + case SHCRGL_GUEST_FN_INJECT: + { + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr; + /* Fetch parameters. */ + uint32_t u32InjectClientID = pFnCmd->u32ClientID; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid inject cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32InjectClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); + + /* @todo: Verify */ + if (cParams == 1) + { + CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer); + + /* Return the required buffer size always */ + pFnCmd->cbBuffer = cbBuffer; + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + /* the read command is never pended, complete it right away */ + pHdr->result = rc; + + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + + /* @todo: Verify */ + if (cParams == 2) + { + CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr; + VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + VBOXCMDVBVA_CRCMD_BUFFER *pWbBuf = &pCmd->aBuffers[2]; + + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + uint32_t cbWriteback = pWbBuf->cbBuffer; + char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write_read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid write buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbWriteback); + if (!pWriteback) + { + crWarning("invalid writeback buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + pHdr->result = rc; + return VINF_SUCCESS; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, false); + rc = crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return rc; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_SET_VERSION: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + case SHCRGL_GUEST_FN_SET_PID: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + default: + { + crWarning("invalid function"); + rc = VERR_NOT_IMPLEMENTED; + break; + } - /* no need to do anything here actually */ } + + /* we can be on fail only here */ + CRASSERT(RT_FAILURE(rc)); + pHdr->result = rc; + + return rc; +} + +static DECLCALLBACK(int) crVBoxCrCmdEnable(HVBOXCRCMDSVR hSvr, VBOXCRCMD_SVRENABLE_INFO *pInfo) +{ + cr_server.CrCmdClientInfo = *pInfo; + + crVBoxServerDefaultContextSet(); + return VINF_SUCCESS; } +static DECLCALLBACK(int) crVBoxCrCmdDisable(HVBOXCRCMDSVR hSvr) +{ + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + memset(&cr_server.CrCmdClientInfo, 0, sizeof (cr_server.CrCmdClientInfo)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdHostCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + return crVBoxServerHostCtl((VBOXCRCMDCTL*)pCmd, cbCmd); +} + +static DECLCALLBACK(int) crVBoxCrCmdGuestCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + VBOXCMDVBVA_3DCTL *pCtl = (VBOXCMDVBVA_3DCTL*)pCmd; + if (cbCmd < sizeof (VBOXCMDVBVA_3DCTL)) + { + WARN(("invalid buffer size")); + return VERR_INVALID_PARAMETER; + } + + switch (pCtl->u32Type) + { + case VBOXCMDVBVA3DCTL_TYPE_CONNECT: + { + VBOXCMDVBVA_3DCTL_CONNECT *pConnect = (VBOXCMDVBVA_3DCTL_CONNECT*)pCtl; + + return VERR_NOT_SUPPORTED; + } + case VBOXCMDVBVA3DCTL_TYPE_DISCONNECT: + { + return VERR_NOT_SUPPORTED; + } + case VBOXCMDVBVA3DCTL_TYPE_CMD: + { + VBOXCMDVBVA_3DCTL_CMD *p3DCmd; + if (cbCmd < sizeof (VBOXCMDVBVA_3DCTL_CMD)) + { + WARN(("invalid size")); + return VERR_INVALID_PARAMETER; + } + + p3DCmd = (VBOXCMDVBVA_3DCTL_CMD*)pCmd; + + return crVBoxCrCmdCmd(NULL, &p3DCmd->Cmd, cbCmd - RT_OFFSETOF(VBOXCMDVBVA_3DCTL_CMD, Cmd)); + } + default: + WARN(("invalid function")); + return VERR_INVALID_PARAMETER; + } +} + +static DECLCALLBACK(int) crVBoxCrCmdSaveState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM) +{ + AssertFailed(); + return VERR_NOT_IMPLEMENTED; +} + +static DECLCALLBACK(int) crVBoxCrCmdLoadState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM, uint32_t u32Version) +{ + AssertFailed(); + return VERR_NOT_IMPLEMENTED; +} + + +static DECLCALLBACK(int) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd) +{ + AssertFailed(); + switch (pCmd->u8OpCode) + { + case VBOXCMDVBVA_OPTYPE_CRCMD: + { + VBOXCMDVBVA_CRCMD *pCrCmdDr; + VBOXCMDVBVA_CRCMD_CMD *pCrCmd; + int rc; + pCrCmdDr = (VBOXCMDVBVA_CRCMD*)pCmd; + pCrCmd = &pCrCmdDr->Cmd; + if (cbCmd < sizeof (VBOXCMDVBVA_CRCMD)) + { + WARN(("invalid buffer size")); + pCmd->u.i8Result = -1; + break; + } + rc = crVBoxServerCmdVbvaCrCmdProcess(pCrCmd, cbCmd - RT_OFFSETOF(VBOXCMDVBVA_CRCMD, Cmd)); + if (RT_SUCCESS(rc)) + { + /* success */ + pCmd->u.i8Result = 0; + } + else + { + WARN(("crVBoxServerCmdVbvaCrCmdProcess failed, rc %d", rc)); + pCmd->u.i8Result = -1; + } + break; + } + case VBOXCMDVBVA_OPTYPE_BLT_OFFPRIMSZFMT_OR_ID: + { + crVBoxServerCrCmdBltProcess(pCmd, cbCmd); + break; + } + default: + WARN(("unsupported command")); + pCmd->u.i8Result = -1; + } + return VINF_SUCCESS; +} -#ifdef VBOX_WITH_CRHGSMI /* We moved all CrHgsmi command processing to crserverlib to keep the logic of dealing with CrHgsmi commands in one place. * * For now we need the notion of CrHgdmi commands in the crserver_lib to be able to complete it asynchronously once it is really processed. @@ -1452,8 +3392,11 @@ DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t * * NOTE: it is ALWAYS responsibility of the crVBoxServerCrHgsmiCmd to complete the command! * */ + + int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t cbCmd) { + int32_t rc; uint32_t cBuffers = pCmd->cBuffers; uint32_t cParams; @@ -1465,14 +3408,16 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c if (!g_pvVRamBase) { - crWarning("g_pvVRamBase is not initialized"); + WARN(("g_pvVRamBase is not initialized")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_STATE); return VINF_SUCCESS; } if (!cBuffers) { - crWarning("zero buffers passed in!"); + WARN(("zero buffers passed in!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } @@ -1483,14 +3428,16 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); if (!pHdr) { - crWarning("invalid header buffer!"); + WARN(("invalid header buffer!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } if (cbHdr < sizeof (*pHdr)) { - crWarning("invalid header buffer size!"); + WARN(("invalid header buffer size!")); + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); return VINF_SUCCESS; } @@ -1502,7 +3449,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c { case SHCRGL_GUEST_FN_WRITE: { - crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); /* @todo: Verify */ if (cParams == 1) @@ -1540,7 +3487,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr); + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1556,7 +3503,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_INJECT: { - crDebug(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); /* @todo: Verify */ if (cParams == 1) @@ -1595,7 +3542,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr); + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1608,7 +3555,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_READ: { - crDebug(("svcCall: SHCRGL_GUEST_FN_READ\n")); + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); /* @todo: Verify */ if (cParams == 1) @@ -1651,6 +3598,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c /* the read command is never pended, complete it right away */ pHdr->result = rc; + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); return VINF_SUCCESS; } @@ -1662,7 +3610,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c case SHCRGL_GUEST_FN_WRITE_READ: { - crDebug(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); /* @todo: Verify */ if (cParams == 2) @@ -1715,7 +3663,7 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c pClient->conn->pBuffer = pBuffer; pClient->conn->cbBuffer = cbBuffer; - CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback); + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, true); rc = crVBoxServerInternalClientWriteRead(pClient); CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); return rc; @@ -1752,8 +3700,24 @@ int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t c /* we can be on fail only here */ CRASSERT(RT_FAILURE(rc)); pHdr->result = rc; + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); return rc; + +} + +static DECLCALLBACK(bool) crVBoxServerHasData() +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + for (; + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + if (CrFbHas3DData(hFb)) + return true; + } + + return false; } int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t cbCtl) @@ -1767,6 +3731,14 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)pCtl; g_pvVRamBase = (uint8_t*)pSetup->pvVRamBase; g_cbVRam = pSetup->cbVRam; + pSetup->CrCmdServerInfo.hSvr = NULL; + pSetup->CrCmdServerInfo.pfnEnable = crVBoxCrCmdEnable; + pSetup->CrCmdServerInfo.pfnDisable = crVBoxCrCmdDisable; + pSetup->CrCmdServerInfo.pfnCmd = crVBoxCrCmdCmd; + pSetup->CrCmdServerInfo.pfnHostCtl = crVBoxCrCmdHostCtl; + pSetup->CrCmdServerInfo.pfnGuestCtl = crVBoxCrCmdGuestCtl; + pSetup->CrCmdServerInfo.pfnSaveState = crVBoxCrCmdSaveState; + pSetup->CrCmdServerInfo.pfnLoadState = crVBoxCrCmdLoadState; rc = VINF_SUCCESS; break; } @@ -1774,11 +3746,14 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END: rc = VINF_SUCCESS; break; - case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION: + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB: { - PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION)pCtl; + PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB)pCtl; g_hCrHgsmiCompletion = pSetup->hCompletion; g_pfnCrHgsmiCompletion = pSetup->pfnCompletion; + + pSetup->MainInterface.pfnHasData = crVBoxServerHasData; + rc = VINF_SUCCESS; break; } @@ -1795,4 +3770,40 @@ int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t c * or Hgcm Host Fast Call commands that do require completion. All this details are hidden here */ return rc; } + +int32_t crVBoxServerHgcmEnable(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hRHCmd, PFNVBOXCRCMDCTL_REMAINING_HOST_COMMAND pfnRHCmd) +{ + int rc = VINF_SUCCESS; + uint8_t* pCtl; + uint32_t cbCtl; + + if (cr_server.numClients) + { + WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients)); + return VERR_INVALID_STATE; + } + + for (pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc); pCtl; pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc)) + { + rc = crVBoxCrCmdHostCtl(NULL, pCtl, cbCtl); + } + + crVBoxServerDefaultContextSet(); + + return VINF_SUCCESS; +} + +int32_t crVBoxServerHgcmDisable() +{ + if (cr_server.numClients) + { + WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients)); + return VERR_INVALID_STATE; + } + + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + return VINF_SUCCESS; +} + #endif diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c index 20a71e3f..e9385996 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c @@ -49,7 +49,40 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchGetChromiumParametervCR(GLenum tar CRASSERT(bytes >= 0); CRASSERT(bytes < 4096); - cr_server.head_spu->dispatch_table.GetChromiumParametervCR( target, index, type, count, local_storage ); + switch (target) + { + case GL_DBG_CHECK_BREAK_CR: + { + if (bytes > 0) + { + GLubyte *pbRc = local_storage; + GLuint *puRc = (GLuint *)(bytes >=4 ? local_storage : NULL); + int rc; + memset(local_storage, 0, bytes); + if (cr_server.RcToGuestOnce) + { + rc = cr_server.RcToGuestOnce; + cr_server.RcToGuestOnce = 0; + } + else + { + rc = cr_server.RcToGuest; + } + if (puRc) + *puRc = rc; + else + *pbRc = !!rc; + } + else + { + crWarning("zero bytes for GL_DBG_CHECK_BREAK_CR"); + } + break; + } + default: + cr_server.head_spu->dispatch_table.GetChromiumParametervCR( target, index, type, count, local_storage ); + break; + } crServerReturnValue( local_storage, bytes ); } @@ -96,13 +129,13 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target break; case GL_GATHER_CONNECT_CR: - /* + /* * We want the last connect to go through, * otherwise we might deadlock in CheckWindowSize() * in the readback spu */ gather_connect_count++; - if (cr_server.only_swap_once && (gather_connect_count != cr_server.numClients)) + if (cr_server.only_swap_once && (gather_connect_count != cr_server.numClients)) { break; } @@ -122,8 +155,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target const GLfloat *v = (const GLfloat *) values; const int eye = v[1] == 0.0 ? 0 : 1; crMatrixInitFromFloats(&cr_server.viewMatrix[eye], v + 2); - - crDebug("Got GL_SERVER_VIEW_MATRIX_CR:\n" + + crDebug("Got GL_SERVER_VIEW_MATRIX_CR:\n" " %f %f %f %f\n" " %f %f %f %f\n" " %f %f %f %f\n" @@ -160,8 +193,8 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target const GLfloat *v = (const GLfloat *) values; const int eye = v[1] == 0.0 ? 0 : 1; crMatrixInitFromFloats(&cr_server.projectionMatrix[eye], v + 2); - - crDebug("Got GL_SERVER_PROJECTION_MATRIX_CR:\n" + + crDebug("Got GL_SERVER_PROJECTION_MATRIX_CR:\n" " %f %f %f %f\n" " %f %f %f %f\n" " %f %f %f %f\n" @@ -196,7 +229,7 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target float right = 2.0f * znear / x + left; float bottom = znear * (b - 1.0f) / y; float top = 2.0f * znear / y + bottom; - crDebug("Frustum: left, right, bottom, top, near, far: %f, %f, %f, %f, %f, %f", left, right, bottom, top, znear, zfar); + crDebug("Frustum: left, right, bottom, top, near, far: %f, %f, %f, %f, %f, %f", left, right, bottom, top, znear, zfar); } else { /* Todo: Add debug output for orthographic projection*/ @@ -206,6 +239,10 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target cr_server.projectionOverride = GL_TRUE; break; + case GL_HH_SET_TMPCTX_MAKE_CURRENT: + /*we should not receive it from the guest! */ + break; + default: /* Pass the parameter info to the head SPU */ cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values ); @@ -238,6 +275,12 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameteriCR(GLenum target case GL_SERVER_CURRENT_EYE_CR: cr_server.currentEye = value ? 1 : 0; break; + case GL_HOST_WND_CREATED_HIDDEN_CR: + cr_server.bWindowsInitiallyHidden = value ? 1 : 0; + break; + case GL_HH_SET_DEFAULT_SHARED_CTX: + crWarning("Recieved GL_HH_SET_DEFAULT_SHARED_CTX from guest, ignoring"); + break; default: /* Pass the parameter info to the head SPU */ cr_server.head_spu->dispatch_table.ChromiumParameteriCR( target, value ); @@ -263,14 +306,6 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameterfCR(GLenum target } } -void crServerCreateInfoDeleteCB(void *data) -{ - CRCreateInfo_t *pCreateInfo = (CRCreateInfo_t *) data; - if (pCreateInfo->pszDpyName) - crFree(pCreateInfo->pszDpyName); - crFree(pCreateInfo); -} - GLint crServerGenerateID(GLint *pCounter) { return (*pCounter)++; @@ -284,14 +319,14 @@ static int copynum=0; #endif # ifdef DEBUG_misha -# define CR_CHECK_BLITS +//# define CR_CHECK_BLITS # include <iprt/assert.h> # undef CRASSERT /* iprt assert's int3 are inlined that is why are more convenient to use since they can be easily disabled individually */ # define CRASSERT Assert # endif -void SERVER_DISPATCH_APIENTRY +void SERVER_DISPATCH_APIENTRY crServerDispatchCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { /*@todo pbo/fbo disabled for now as it's slower, check on other gpus*/ @@ -392,7 +427,7 @@ crServerDispatchCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLi gl->CopyTexImage2D(target, level, GL_RGBA, x, y, width, -height, 0); gl->GenFramebuffersEXT(1, &fboID); gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); - gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, + gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid, level); status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) @@ -460,7 +495,7 @@ crServerDispatchCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLi gl->GetTexImage(target, level, GL_BGRA, GL_UNSIGNED_BYTE, img1); - + for (dRow=yoffset, sRow=y-height-1; dRow<yoffset-height; dRow++, sRow--) { gl->CopyTexSubImage2D(target, level, xoffset, dRow, x, sRow, width, 1); @@ -632,18 +667,252 @@ void crDbgDumpTexImage2D(const char* pszDesc, GLint texTarget, GLint texName, GL } #endif -void SERVER_DISPATCH_APIENTRY +PCR_BLITTER crServerVBoxBlitterGet() +{ + if (!CrBltIsInitialized(&cr_server.Blitter)) + { + CR_BLITTER_CONTEXT Ctx; + int rc; + CRASSERT(cr_server.MainContextInfo.SpuContext); + Ctx.Base.id = cr_server.MainContextInfo.SpuContext; + Ctx.Base.visualBits = cr_server.MainContextInfo.CreateInfo.realVisualBits; + rc = CrBltInit(&cr_server.Blitter, &Ctx, true, true, NULL, &cr_server.TmpCtxDispatch); + if (RT_SUCCESS(rc)) + { + CRASSERT(CrBltIsInitialized(&cr_server.Blitter)); + } + else + { + crWarning("CrBltInit failed, rc %d", rc); + CRASSERT(!CrBltIsInitialized(&cr_server.Blitter)); + return NULL; + } + } + + if (!CrBltMuralGetCurrentInfo(&cr_server.Blitter)->Base.id) + { + CRMuralInfo *dummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CR_BLITTER_WINDOW DummyInfo; + CRASSERT(dummy); + crServerVBoxBlitterWinInit(&DummyInfo, dummy); + CrBltMuralSetCurrentInfo(&cr_server.Blitter, &DummyInfo); + } + + return &cr_server.Blitter; +} + +PCR_BLITTER crServerVBoxBlitterGetInitialized() +{ + if (CrBltIsInitialized(&cr_server.Blitter)) + return &cr_server.Blitter; + return NULL; +} + + +int crServerVBoxBlitterTexInit(CRContext *ctx, CRMuralInfo *mural, PVBOXVR_TEXTURE pTex, GLboolean fDraw) +{ + CRTextureObj *tobj; + CRFramebufferObjectState *pBuf = &ctx->framebufferobject; + GLenum enmBuf; + CRFBOAttachmentPoint *pAp; + GLuint idx; + CRTextureLevel *tl; + CRFramebufferObject *pFBO = fDraw ? pBuf->drawFB : pBuf->readFB; + + if (!pFBO) + { + GLuint hwid; + + if (!mural->fRedirected) + return VERR_NOT_IMPLEMENTED; + + enmBuf = fDraw ? ctx->buffer.drawBuffer : ctx->buffer.readBuffer; + switch (enmBuf) + { + case GL_BACK: + case GL_BACK_RIGHT: + case GL_BACK_LEFT: + hwid = mural->aidColorTexs[CR_SERVER_FBO_BB_IDX(mural)]; + break; + case GL_FRONT: + case GL_FRONT_RIGHT: + case GL_FRONT_LEFT: + hwid = mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]; + break; + default: + crWarning("unsupported enum buf"); + return VERR_NOT_IMPLEMENTED; + break; + } + + if (!hwid) + { + crWarning("offscreen render tex hwid is null"); + return VERR_INVALID_STATE; + } + + pTex->width = mural->width; + pTex->height = mural->height; + pTex->target = GL_TEXTURE_2D; + pTex->hwid = hwid; + return VINF_SUCCESS; + } + + enmBuf = fDraw ? pFBO->drawbuffer[0] : pFBO->readbuffer; + idx = enmBuf - GL_COLOR_ATTACHMENT0_EXT; + if (idx >= CR_MAX_COLOR_ATTACHMENTS) + { + crWarning("idx is invalid %d, using 0", idx); + } + + pAp = &pFBO->color[idx]; + + if (!pAp->name) + { + crWarning("no collor draw attachment"); + return VERR_INVALID_STATE; + } + + if (pAp->level) + { + crWarning("non-zero level not implemented"); + return VERR_NOT_IMPLEMENTED; + } + + tobj = (CRTextureObj*)crHashtableSearch(ctx->shared->textureTable, pAp->name); + if (!tobj) + { + crWarning("no texture object found for name %d", pAp->name); + return VERR_INVALID_STATE; + } + + if (tobj->target != GL_TEXTURE_2D && tobj->target != GL_TEXTURE_RECTANGLE_NV) + { + crWarning("non-texture[rect|2d] not implemented"); + return VERR_NOT_IMPLEMENTED; + } + + CRASSERT(tobj->hwid); + + tl = tobj->level[0]; + pTex->width = tl->width; + pTex->height = tl->height; + pTex->target = tobj->target; + pTex->hwid = tobj->hwid; + + return VINF_SUCCESS; +} + +int crServerVBoxBlitterBlitCurrentCtx(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + PCR_BLITTER pBlitter; + CR_BLITTER_CONTEXT Ctx; + CRMuralInfo *mural; + CRContext *ctx = crStateGetCurrent(); + PVBOXVR_TEXTURE pDrawTex, pReadTex; + VBOXVR_TEXTURE DrawTex, ReadTex; + int rc; + GLuint idDrawFBO, idReadFBO; + CR_BLITTER_WINDOW BltInfo; + + if (mask != GL_COLOR_BUFFER_BIT) + { + crWarning("not supported blit mask %d", mask); + return VERR_NOT_IMPLEMENTED; + } + + if (!cr_server.curClient) + { + crWarning("no current client"); + return VERR_INVALID_STATE; + } + mural = cr_server.curClient->currentMural; + if (!mural) + { + crWarning("no current mural"); + return VERR_INVALID_STATE; + } + + rc = crServerVBoxBlitterTexInit(ctx, mural, &DrawTex, GL_TRUE); + if (RT_SUCCESS(rc)) + { + pDrawTex = &DrawTex; + } + else + { + crWarning("crServerVBoxBlitterTexInit failed for draw"); + return rc; + } + + rc = crServerVBoxBlitterTexInit(ctx, mural, &ReadTex, GL_FALSE); + if (RT_SUCCESS(rc)) + { + pReadTex = &ReadTex; + } + else + { +// crWarning("crServerVBoxBlitterTexInit failed for read"); + return rc; + } + + pBlitter = crServerVBoxBlitterGet(); + if (!pBlitter) + { + crWarning("crServerVBoxBlitterGet failed"); + return VERR_GENERAL_FAILURE; + } + + crServerVBoxBlitterWinInit(&BltInfo, mural); + + crServerVBoxBlitterCtxInit(&Ctx, cr_server.curClient->currentCtxInfo); + + CrBltMuralSetCurrentInfo(pBlitter, &BltInfo); + + idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + + crStateSwitchPrepare(NULL, ctx, idDrawFBO, idReadFBO); + + rc = CrBltEnter(pBlitter); + if (RT_SUCCESS(rc)) + { + RTRECT ReadRect, DrawRect; + ReadRect.xLeft = srcX0; + ReadRect.yTop = srcY0; + ReadRect.xRight = srcX1; + ReadRect.yBottom = srcY1; + DrawRect.xLeft = dstX0; + DrawRect.yTop = dstY0; + DrawRect.xRight = dstX1; + DrawRect.yBottom = dstY1; + CrBltBlitTexTex(pBlitter, pReadTex, &ReadRect, pDrawTex, &DrawRect, 1, CRBLT_FLAGS_FROM_FILTER(filter)); + CrBltLeave(pBlitter); + } + else + { + crWarning("CrBltEnter failed rc %d", rc); + } + + crStateSwitchPostprocess(ctx, NULL, idDrawFBO, idReadFBO); + + return rc; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + CRContext *ctx = crStateGetCurrent(); + bool fTryBlitter = false; #ifdef CR_CHECK_BLITS // { SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; GLint rfb=0, dfb=0, dtex=0, dlev=-1, rtex=0, rlev=-1, rb=0, db=0, ppb=0, pub=0, vp[4], otex, dstw, dsth; GLint sdtex=0, srtex=0; GLenum dStatus, rStatus; - CRContext *ctx = crStateGetCurrent(); CRTextureObj *tobj = 0; CRTextureLevel *tl = 0; @@ -847,10 +1116,60 @@ crServerDispatchBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint gl->BindTexture(GL_TEXTURE_2D, otex); #endif + if (srcY0 > srcY1) + { + /* work around Intel driver bug on Linux host */ + if (1 || dstY0 > dstY1) + { + /* use srcY1 < srcY2 && dstY1 < dstY2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcY0; + srcY0 = srcY1; + srcY1 = tmp; + tmp = dstY0; + dstY0 = dstY1; + dstY1 = tmp; + } + } + + if (srcX0 > srcX1) + { + if (dstX0 > dstX1) + { + /* use srcX1 < srcX2 && dstX1 < dstX2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcX0; + srcX0 = srcX1; + srcX1 = tmp; + tmp = dstX0; + dstX0 = dstX1; + dstX1 = tmp; + } + } + + if (cr_server.fBlitterMode) + { + fTryBlitter = true; + } + + if (fTryBlitter) + { + int rc = crServerVBoxBlitterBlitCurrentCtx(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + if (RT_SUCCESS(rc)) + goto my_exit; + } + + if (ctx->viewport.scissorTest) + cr_server.head_spu->dispatch_table.Disable(GL_SCISSOR_TEST); + cr_server.head_spu->dispatch_table.BlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + if (ctx->viewport.scissorTest) + cr_server.head_spu->dispatch_table.Enable(GL_SCISSOR_TEST); + + +my_exit: + //#ifdef CR_CHECK_BLITS // crDbgDumpTexImage2D("<== src tex:", GL_TEXTURE_2D, rtex, true); // crDbgDumpTexImage2D("<== dst tex:", GL_TEXTURE_2D, dtex, true); @@ -863,6 +1182,7 @@ crServerDispatchBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint gl->BindTexture(GL_TEXTURE_2D, otex); crFree(img); #endif + return; } void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffer( GLenum mode ) @@ -871,27 +1191,69 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffer( GLenum mode ) if (!crStateGetCurrent()->framebufferobject.drawFB) { - if (mode == GL_FRONT || mode == GL_FRONT_LEFT) + if (mode == GL_FRONT || mode == GL_FRONT_LEFT || mode == GL_FRONT_RIGHT) cr_server.curClient->currentMural->bFbDraw = GL_TRUE; - if (cr_server.curClient->currentMural->bUseFBO && crServerIsRedirectedToFBO() - && cr_server.curClient->currentMural->idFBO) + if (crServerIsRedirectedToFBO() + && cr_server.curClient->currentMural->aidFBOs[0]) { + CRMuralInfo *mural = cr_server.curClient->currentMural; + GLint iBufferNeeded = -1; switch (mode) { case GL_BACK: case GL_BACK_LEFT: + case GL_BACK_RIGHT: mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); break; case GL_FRONT: case GL_FRONT_LEFT: - crDebug("Setting GL_FRONT with FBO mode! (0x%x)", mode); + case GL_FRONT_RIGHT: + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural); + break; + case GL_NONE: + crDebug("DrawBuffer: GL_NONE"); + break; + case GL_AUX0: + crDebug("DrawBuffer: GL_AUX0"); + break; + case GL_AUX1: + crDebug("DrawBuffer: GL_AUX1"); + break; + case GL_AUX2: + crDebug("DrawBuffer: GL_AUX2"); + break; + case GL_AUX3: + crDebug("DrawBuffer: GL_AUX3"); + break; + case GL_LEFT: + crWarning("DrawBuffer: GL_LEFT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_RIGHT: + crWarning("DrawBuffer: GL_RIGHT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT_AND_BACK: + crWarning("DrawBuffer: GL_FRONT_AND_BACK not supported properly"); mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); break; default: - crWarning("unexpected mode! 0x%x", mode); + crWarning("DrawBuffer: unexpected mode! 0x%x", mode); + iBufferNeeded = mural->iCurDrawBuffer; break; } + + if (iBufferNeeded != mural->iCurDrawBuffer) + { + mural->iCurDrawBuffer = iBufferNeeded; + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded)); + } } } @@ -902,25 +1264,661 @@ void SERVER_DISPATCH_APIENTRY crServerDispatchReadBuffer( GLenum mode ) { crStateReadBuffer( mode ); - if (cr_server.curClient->currentMural->bUseFBO && crServerIsRedirectedToFBO() - && cr_server.curClient->currentMural->idFBO + if (crServerIsRedirectedToFBO() + && cr_server.curClient->currentMural->aidFBOs[0] && !crStateGetCurrent()->framebufferobject.readFB) { + CRMuralInfo *mural = cr_server.curClient->currentMural; + GLint iBufferNeeded = -1; switch (mode) { case GL_BACK: case GL_BACK_LEFT: + case GL_BACK_RIGHT: mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); break; case GL_FRONT: case GL_FRONT_LEFT: - crWarning("GL_FRONT not supported for FBO mode!"); + case GL_FRONT_RIGHT: mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural); + break; + case GL_NONE: + crDebug("ReadBuffer: GL_NONE"); + break; + case GL_AUX0: + crDebug("ReadBuffer: GL_AUX0"); + break; + case GL_AUX1: + crDebug("ReadBuffer: GL_AUX1"); + break; + case GL_AUX2: + crDebug("ReadBuffer: GL_AUX2"); + break; + case GL_AUX3: + crDebug("ReadBuffer: GL_AUX3"); + break; + case GL_LEFT: + crWarning("ReadBuffer: GL_LEFT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_RIGHT: + crWarning("ReadBuffer: GL_RIGHT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT_AND_BACK: + crWarning("ReadBuffer: GL_FRONT_AND_BACK not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); break; default: - crWarning("unexpected mode! 0x%x", mode); + crWarning("ReadBuffer: unexpected mode! 0x%x", mode); + iBufferNeeded = mural->iCurDrawBuffer; break; } + + Assert(CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + if (iBufferNeeded != mural->iCurReadBuffer) + { + mural->iCurReadBuffer = iBufferNeeded; + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded)); + } } cr_server.head_spu->dispatch_table.ReadBuffer( mode ); } + +GLenum SERVER_DISPATCH_APIENTRY crServerDispatchGetError( void ) +{ + GLenum retval, err; + CRContext *ctx = crStateGetCurrent(); + retval = ctx->error; + + err = cr_server.head_spu->dispatch_table.GetError(); + if (retval == GL_NO_ERROR) + retval = err; + else + ctx->error = GL_NO_ERROR; + + /* our impl has a single error flag, so we just loop here to reset all error flags to no_error */ + while (err != GL_NO_ERROR) + err = cr_server.head_spu->dispatch_table.GetError(); + + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +void SERVER_DISPATCH_APIENTRY +crServerMakeTmpCtxCurrent( GLint window, GLint nativeWindow, GLint context ) +{ + CRContext *pCtx = crStateGetCurrent(); + CRContext *pCurCtx = NULL; + GLuint idDrawFBO = 0, idReadFBO = 0; + int fDoPrePostProcess = 0; + + if (pCtx) + { + CRMuralInfo *pCurrentMural = cr_server.currentMural; + + pCurCtx = cr_server.currentCtxInfo ? cr_server.currentCtxInfo->pContext : cr_server.MainContextInfo.pContext; + Assert(pCurCtx == pCtx); + + if (!context) + { + if (pCurrentMural) + { + Assert(cr_server.currentCtxInfo); + context = cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext; + window = pCurrentMural->spuWindow; + } + else + { + CRMuralInfo * pDummy; + Assert(!cr_server.currentCtxInfo); + pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + context = cr_server.MainContextInfo.SpuContext; + window = pDummy->spuWindow; + } + + + fDoPrePostProcess = -1; + } + else + { + fDoPrePostProcess = 1; + } + + if (pCurrentMural) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + } + else + { + /* this is a GUI thread, so no need to do anything here */ + } + + if (fDoPrePostProcess > 0) + crStateSwitchPrepare(NULL, pCurCtx, idDrawFBO, idReadFBO); + + cr_server.head_spu->dispatch_table.MakeCurrent( window, nativeWindow, context); + + if (fDoPrePostProcess < 0) + crStateSwitchPostprocess(pCurCtx, NULL, idDrawFBO, idReadFBO); +} + +void crServerInitTmpCtxDispatch() +{ + MakeCurrentFunc_t pfnMakeCurrent; + + crSPUInitDispatchTable(&cr_server.TmpCtxDispatch); + crSPUCopyDispatchTable(&cr_server.TmpCtxDispatch, &cr_server.head_spu->dispatch_table); + cr_server.TmpCtxDispatch.MakeCurrent = crServerMakeTmpCtxCurrent; + + pfnMakeCurrent = crServerMakeTmpCtxCurrent; + cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_TMPCTX_MAKE_CURRENT, GL_BYTE, sizeof (void*), &pfnMakeCurrent); + +} + +/* dump stuff */ +#ifdef VBOX_WITH_CRSERVER_DUMPER + +/* first four bits are buffer dump config + * second four bits are texture dump config + * config flags: + * 1 - blit on enter + * 2 - blit on exit + * + * + * Example: + * + * 0x03 - dump buffer on enter and exit + * 0x22 - dump texture and buffer on exit */ + +int64_t g_CrDbgDumpPid = 0; +unsigned long g_CrDbgDumpEnabled = 0; +unsigned long g_CrDbgDumpDraw = 0 +#if 0 + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM +#endif + ; +#if 0 + | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER + | CR_SERVER_DUMP_F_DRAWEL + | CR_SERVER_DUMP_F_SHADER_SOURCE + ; +#endif +unsigned long g_CrDbgDumpDrawFramesSettings = CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER; +unsigned long g_CrDbgDumpDrawFramesAppliedSettings = 0; +unsigned long g_CrDbgDumpDrawFramesSavedInitSettings = 0; +unsigned long g_CrDbgDumpDrawFramesCount = 0; + +uint32_t g_CrDbgDumpDrawCount = 0; +uint32_t g_CrDbgDumpDumpOnCount = 10; +uint32_t g_CrDbgDumpDumpOnCountEnabled = 0; +uint32_t g_CrDbgDumpDumpOnCountPerform = 0; +uint32_t g_CrDbgDumpDrawFlags = CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_SHADER_SOURCE + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM + | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER + | CR_SERVER_DUMP_F_DRAWEL + | CR_SERVER_DUMP_F_TEXPRESENT; + +void crServerDumpCheckTerm() +{ + if (!CrBltIsInitialized(&cr_server.RecorderBlitter)) + return; + + CrBltTerm(&cr_server.RecorderBlitter); +} + +int crServerDumpCheckInit() +{ + int rc; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRMuralInfo *pBlitterMural; + + if (!CrBltIsInitialized(&cr_server.RecorderBlitter)) + { + pBlitterMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pBlitterMural) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + crServerVBoxBlitterWinInit(&BltWin, pBlitterMural); + crServerVBoxBlitterCtxInit(&BltCtx, &cr_server.MainContextInfo); + + rc = CrBltInit(&cr_server.RecorderBlitter, &BltCtx, true, true, NULL, &cr_server.TmpCtxDispatch); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltInit failed rc %d", rc); + return rc; + } + + rc = CrBltMuralSetCurrentInfo(&cr_server.RecorderBlitter, &BltWin); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltMuralSetCurrentInfo failed rc %d", rc); + return rc; + } + } + +#if 0 + crDmpDbgPrintInit(&cr_server.DbgPrintDumper); + cr_server.pDumper = &cr_server.DbgPrintDumper.Base; +#else + if (!crDmpHtmlIsInited(&cr_server.HtmlDumper)) + { + static int cCounter = 0; +// crDmpHtmlInit(&cr_server.HtmlDumper, "S:\\projects\\virtualbox\\3d\\dumps\\1", "index.html"); + crDmpHtmlInitF(&cr_server.HtmlDumper, "/Users/oracle-mac/vbox/dump/1", "index%d.html", cCounter); + cr_server.pDumper = &cr_server.HtmlDumper.Base; + ++cCounter; + } +#endif + + crRecInit(&cr_server.Recorder, &cr_server.RecorderBlitter, &cr_server.TmpCtxDispatch, cr_server.pDumper); + return VINF_SUCCESS; +} + +void crServerDumpShader(GLint id) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpShader(&cr_server.Recorder, ctx, id, 0); +} + +void crServerDumpProgram(GLint id) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpProgram(&cr_server.Recorder, ctx, id, 0); +} + +void crServerDumpCurrentProgram() +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpCurrentProgram(&cr_server.Recorder, ctx); +} + +void crServerDumpRecompileDumpCurrentProgram() +{ + crDmpStrF(cr_server.Recorder.pDumper, "==Dump(1)=="); + crServerRecompileCurrentProgram(); + crServerDumpCurrentProgramUniforms(); + crServerDumpCurrentProgramAttribs(); + crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(1)"); + crServerRecompileCurrentProgram(); + crDmpStrF(cr_server.Recorder.pDumper, "Dump(2)"); + crServerRecompileCurrentProgram(); + crServerDumpCurrentProgramUniforms(); + crServerDumpCurrentProgramAttribs(); + crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(2)"); +} + +void crServerRecompileCurrentProgram() +{ + CRContext *ctx = crStateGetCurrent(); + crRecRecompileCurrentProgram(&cr_server.Recorder, ctx); +} + +void crServerDumpCurrentProgramUniforms() +{ + CRContext *ctx = crStateGetCurrent(); + crDmpStrF(cr_server.Recorder.pDumper, "==Uniforms=="); + crRecDumpCurrentProgramUniforms(&cr_server.Recorder, ctx); + crDmpStrF(cr_server.Recorder.pDumper, "==Done Uniforms=="); +} + +void crServerDumpCurrentProgramAttribs() +{ + CRContext *ctx = crStateGetCurrent(); + crDmpStrF(cr_server.Recorder.pDumper, "==Attribs=="); + crRecDumpCurrentProgramAttribs(&cr_server.Recorder, ctx); + crDmpStrF(cr_server.Recorder.pDumper, "==Done Attribs=="); +} + +void crServerDumpState() +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpGlGetState(&cr_server.Recorder, ctx); + crRecDumpGlEnableState(&cr_server.Recorder, ctx); +} + +void crServerDumpDrawel(const char*pszFormat, ...) +{ + CRContext *ctx = crStateGetCurrent(); + va_list pArgList; + va_start(pArgList, pszFormat); + crRecDumpVertAttrV(&cr_server.Recorder, ctx, pszFormat, pArgList); + va_end(pArgList); +} + +void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpVertAttrv(&cr_server.Recorder, ctx, idx, pszElFormat, cbEl, pvVal, cVal); +} + +void crServerDumpBuffer(int idx) +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRContext *ctx = crStateGetCurrent(); + GLint idFBO; + GLint idTex; + VBOXVR_TEXTURE RedirTex; + int rc = crServerDumpCheckInit(); + idx = idx >= 0 ? idx : crServerMuralFBOIdxFromBufferName(cr_server.currentMural, pCtxInfo->pContext->buffer.drawBuffer); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + if (idx < 0) + { + crWarning("neg idx, unsupported"); + return; + } + + idFBO = CR_SERVER_FBO_FOR_IDX(cr_server.currentMural, idx); + idTex = CR_SERVER_FBO_TEX_FOR_IDX(cr_server.currentMural, idx); + + crServerVBoxBlitterWinInit(&BltWin, cr_server.currentMural); + crServerVBoxBlitterCtxInit(&BltCtx, pCtxInfo); + + RedirTex.width = cr_server.currentMural->fboWidth; + RedirTex.height = cr_server.currentMural->fboHeight; + RedirTex.target = GL_TEXTURE_2D; + RedirTex.hwid = idTex; + + crRecDumpBuffer(&cr_server.Recorder, ctx, &BltCtx, &BltWin, idFBO, idTex ? &RedirTex : NULL); +} + +void crServerDumpTexture(const VBOXVR_TEXTURE *pTex) +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRContext *ctx = crStateGetCurrent(); + int rc = crServerDumpCheckInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + crServerVBoxBlitterWinInit(&BltWin, cr_server.currentMural); + crServerVBoxBlitterCtxInit(&BltCtx, pCtxInfo); + + crRecDumpTextureF(&cr_server.Recorder, pTex, &BltCtx, &BltWin, "Tex (%d x %d), hwid (%d) target %#x", pTex->width, pTex->height, pTex->hwid, pTex->target); +} + +void crServerDumpTextures() +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRContext *ctx = crStateGetCurrent(); + int rc = crServerDumpCheckInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + crServerVBoxBlitterWinInit(&BltWin, cr_server.currentMural); + crServerVBoxBlitterCtxInit(&BltCtx, pCtxInfo); + + crRecDumpTextures(&cr_server.Recorder, ctx, &BltCtx, &BltWin); +} + +void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper) +{ + if (CR_SERVER_DUMP_F_DRAW_LEAVE_ALL & event) + { + g_CrDbgDumpDumpOnCountPerform = 0; + } +} + +bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper) +{ + if ((CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER & event) + || (CR_SERVER_DUMP_F_TEXPRESENT & event)) + { + if (g_CrDbgDumpDumpOnCountEnabled == 1) + g_CrDbgDumpDumpOnCountEnabled = 2; + else if (g_CrDbgDumpDumpOnCountEnabled) + { + g_CrDbgDumpDumpOnCountEnabled = 0; + if (cr_server.pDumper == &cr_server.HtmlDumper.Base) + { + crDmpHtmlTerm(&cr_server.HtmlDumper); + cr_server.pDumper = NULL; + } + } + + g_CrDbgDumpDrawCount = 0; + } + else if (CR_SERVER_DUMP_F_DRAW_ENTER_ALL & event) + { + if (g_CrDbgDumpDumpOnCountEnabled == 2) + { + if (g_CrDbgDumpDumpOnCount == g_CrDbgDumpDrawCount) + { + g_CrDbgDumpDumpOnCountPerform = 1; + } + ++g_CrDbgDumpDrawCount; + } + } + if (g_CrDbgDumpDumpOnCountPerform) + { + if (g_CrDbgDumpDrawFlags & event) + return true; + } + return CR_SERVER_DUMP_DEFAULT_FILTER_OP(event); +} + +bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper) +{ + if (g_CrDbgDumpDumpOnCountPerform) + { + if (g_CrDbgDumpDrawFlags & event) + return true; + } + return CR_SERVER_DUMP_DEFAULT_FILTER_DMP(event); +} + +void crServerDumpFramesCheck() +{ + if (!g_CrDbgDumpDrawFramesCount) + return; + + if (!g_CrDbgDumpDrawFramesAppliedSettings) + { + if (!g_CrDbgDumpDrawFramesSettings) + { + crWarning("g_CrDbgDumpDrawFramesSettings is NULL, bump will not be started"); + g_CrDbgDumpDrawFramesCount = 0; + return; + } + + g_CrDbgDumpDrawFramesSavedInitSettings = g_CrDbgDumpDraw; + g_CrDbgDumpDrawFramesAppliedSettings = g_CrDbgDumpDrawFramesSettings; + g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSettings; + crDmpStrF(cr_server.Recorder.pDumper, "***Starting draw dump for %d frames, settings(0x%x)", g_CrDbgDumpDrawFramesCount, g_CrDbgDumpDraw); + return; + } + + --g_CrDbgDumpDrawFramesCount; + + if (!g_CrDbgDumpDrawFramesCount) + { + crDmpStrF(cr_server.Recorder.pDumper, "***Stop draw dump"); + g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSavedInitSettings; + g_CrDbgDumpDrawFramesAppliedSettings = 0; + } +} +#endif + +GLvoid crServerSpriteCoordReplEnable(GLboolean fEnable) +{ + CRContext *g = crStateGetCurrent(); + CRTextureState *t = &(g->texture); + GLuint curTextureUnit = t->curTextureUnit; + GLuint curTextureUnitRestore = curTextureUnit; + GLuint i; + + for (i = 0; i < g->limits.maxTextureUnits; ++i) + { + if (g->point.coordReplacement[i]) + { + if (i != curTextureUnit) + { + curTextureUnit = i; + cr_server.head_spu->dispatch_table.ActiveTextureARB( i + GL_TEXTURE0_ARB ); + } + + cr_server.head_spu->dispatch_table.TexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, (GLint)fEnable); + } + } + + if (curTextureUnit != curTextureUnitRestore) + { + cr_server.head_spu->dispatch_table.ActiveTextureARB( curTextureUnitRestore + GL_TEXTURE0_ARB ); + } +} + +GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawArrays(GLenum mode, GLint first, GLsizei count) +{ +#ifdef DEBUG + GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + Assert(GL_FRAMEBUFFER_COMPLETE == status); +#endif + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + CR_SERVER_DUMP_DRAW_ENTER(); + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawArrays(mode, first, count);); + CR_SERVER_DUMP_DRAW_LEAVE(); + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices) +{ +#ifdef DEBUG + GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + Assert(GL_FRAMEBUFFER_COMPLETE == status); +#endif + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + CR_SERVER_DUMP_DRAW_ENTER(); + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawElements(mode, count, type, indices);); + CR_SERVER_DUMP_DRAW_LEAVE(); + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchEnd( void ) +{ + CRContext *g = crStateGetCurrent(); + GLenum mode = g->current.mode; + + crStateEnd(); + cr_server.head_spu->dispatch_table.End(); + + CR_SERVER_DUMP_DRAW_LEAVE(); + + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBegin(GLenum mode) +{ +#ifdef DEBUG + CRContext *ctx = crStateGetCurrent(); + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + + if (ctx->program.vpProgramBinding) + { + AssertRelease(ctx->program.currentVertexProgram); + + if (ctx->program.currentVertexProgram->isARBprogram) + { + GLint pid=-1; + gl->GetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &pid); + + if (pid != ctx->program.currentVertexProgram->id) + { + crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id); + } + AssertRelease(pid == ctx->program.currentVertexProgram->id); + } + else + { + GLint pid=-1; + + gl->GetIntegerv(GL_VERTEX_PROGRAM_BINDING_NV, &pid); + if (pid != ctx->program.currentVertexProgram->id) + { + crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id); + } + AssertRelease(pid == ctx->program.currentVertexProgram->id); + } + } + else if (ctx->glsl.activeProgram) + { + GLint pid=-1; + + gl->GetIntegerv(GL_CURRENT_PROGRAM, &pid); + crDebug("pid %i, state: id %i, hwid %i", pid, ctx->glsl.activeProgram->id, ctx->glsl.activeProgram->hwid); + if (pid != ctx->glsl.activeProgram->hwid) + { + crWarning("pid(%d) != ctx->glsl.activeProgram->hwid(%d)", pid, ctx->glsl.activeProgram->hwid); + } + AssertRelease(pid == ctx->glsl.activeProgram->hwid); + } +#endif + + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + + CR_SERVER_DUMP_DRAW_ENTER(); + + crStateBegin(mode); + cr_server.head_spu->dispatch_table.Begin(mode); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c deleted file mode 100644 index f42ed0c7..00000000 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c +++ /dev/null @@ -1,591 +0,0 @@ -/* $Id: server_muralfbo.c $ */ - -/** @file - * VBox crOpenGL: Window to FBO redirect support. - */ - -/* - * Copyright (C) 2010 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - -#include "server.h" -#include "cr_string.h" -#include "cr_mem.h" -#include "render/renderspu.h" - -static int crServerGetPointScreen(GLint x, GLint y) -{ - int i; - - for (i=0; i<cr_server.screenCount; ++i) - { - if ((x>=cr_server.screen[i].x && x<cr_server.screen[i].x+(int)cr_server.screen[i].w) - && (y>=cr_server.screen[i].y && y<cr_server.screen[i].y+(int)cr_server.screen[i].h)) - { - return i; - } - } - - return -1; -} - -static GLboolean crServerMuralCoverScreen(CRMuralInfo *mural, int sId) -{ - return mural->gX < cr_server.screen[sId].x - && mural->gX+(int)mural->width > cr_server.screen[sId].x+(int)cr_server.screen[sId].w - && mural->gY < cr_server.screen[sId].y - && mural->gY+(int)mural->height > cr_server.screen[sId].y+(int)cr_server.screen[sId].h; -} - -/* Called when a new CRMuralInfo is created - * or when OutputRedirect status is changed. - */ -void crServerSetupOutputRedirect(CRMuralInfo *mural) -{ - /* Unset the previous redirect. */ - if (mural->pvOutputRedirectInstance) - { - cr_server.outputRedirect.CROREnd(mural->pvOutputRedirectInstance); - mural->pvOutputRedirectInstance = NULL; - } - - /* Setup a new redirect. */ - if (cr_server.bUseOutputRedirect) - { - /* Query supported formats. */ - uint32_t cbFormats = 4096; - char *pachFormats = (char *)crAlloc(cbFormats); - - if (pachFormats) - { - int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext, - 0 /* H3DOR_PROP_FORMATS */, // @todo from a header - pachFormats, cbFormats, &cbFormats); - if (RT_SUCCESS(rc)) - { - if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN")) - { - cr_server.outputRedirect.CRORBegin(cr_server.outputRedirect.pvContext, - &mural->pvOutputRedirectInstance, - "H3DOR_FMT_RGBA_TOPDOWN"); // @todo from a header - } - } - - crFree(pachFormats); - } - - /* If this is not NULL then there was a supported format. */ - if (mural->pvOutputRedirectInstance) - { - cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance, - mural->hX, mural->hY, - mural->width, mural->height); - // @todo the code assumes that RTRECT == four of GLInts - cr_server.outputRedirect.CRORVisibleRegion(mural->pvOutputRedirectInstance, - mural->cVisibleRects, (RTRECT *)mural->pVisibleRects); - } - } -} - -void crServerCheckMuralGeometry(CRMuralInfo *mural) -{ - int tlS, brS, trS, blS; - int overlappingScreenCount, primaryS, i; - - if (!mural->width || !mural->height) - return; - - if (cr_server.screenCount<2 && !cr_server.bForceOffscreenRendering) - { - CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId]; - CRASSERT(cr_server.screenCount>0); - - mural->hX = mural->gX-cr_server.screen[0].x; - mural->hY = mural->gY-cr_server.screen[0].y; - - cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y); - - return; - } - - tlS = crServerGetPointScreen(mural->gX, mural->gY); - brS = crServerGetPointScreen(mural->gX+mural->width-1, mural->gY+mural->height-1); - - if (tlS==brS && tlS>=0) - { - overlappingScreenCount = 1; - primaryS = tlS; - } - else - { - trS = crServerGetPointScreen(mural->gX+mural->width-1, mural->gY); - blS = crServerGetPointScreen(mural->gX, mural->gY+mural->height-1); - - primaryS = -1; overlappingScreenCount = 0; - for (i=0; i<cr_server.screenCount; ++i) - { - if ((i==tlS) || (i==brS) || (i==trS) || (i==blS) - || crServerMuralCoverScreen(mural, i)) - { - overlappingScreenCount++; - primaryS = primaryS<0 ? i:primaryS; - } - } - - if (!overlappingScreenCount) - { - primaryS = 0; - } - } - - if (primaryS!=mural->screenId) - { - mural->screenId = primaryS; - - renderspuSetWindowId(cr_server.screen[primaryS].winID); - renderspuReparentWindow(mural->spuWindow); - renderspuSetWindowId(cr_server.screen[0].winID); - } - - mural->hX = mural->gX-cr_server.screen[primaryS].x; - mural->hY = mural->gY-cr_server.screen[primaryS].y; - - if (overlappingScreenCount<2 && !cr_server.bForceOffscreenRendering) - { - CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId]; - - if (mural->bUseFBO) - { - crServerRedirMuralFBO(mural, GL_FALSE); - crServerDeleteMuralFBO(mural); - } - - cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y); - } - else - { - if (mural->spuWindow) - { - if (!mural->bUseFBO) - { - crServerRedirMuralFBO(mural, GL_TRUE); - } - else - { - if (mural->width!=mural->fboWidth - || mural->height!=mural->height) - { - crServerRedirMuralFBO(mural, GL_FALSE); - crServerDeleteMuralFBO(mural); - crServerRedirMuralFBO(mural, GL_TRUE); - } - } - } -#ifdef DEBUG_misha - else - { - Assert(!mural->bUseFBO); - } -#endif - - if (!mural->bUseFBO) - { - CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId]; - - cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y); - } - } - - if (mural->pvOutputRedirectInstance) - { - cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance, - mural->hX, mural->hY, - mural->width, mural->height); - } -} - -GLboolean crServerSupportRedirMuralFBO(void) -{ - static GLboolean fInited = GL_FALSE; - static GLboolean fSupported = GL_FALSE; - if (!fInited) - { - const GLubyte* pExt = cr_server.head_spu->dispatch_table.GetString(GL_REAL_EXTENSIONS); - - fSupported = ( NULL!=crStrstr((const char*)pExt, "GL_ARB_framebuffer_object") - || NULL!=crStrstr((const char*)pExt, "GL_EXT_framebuffer_object")) - && NULL!=crStrstr((const char*)pExt, "GL_ARB_texture_non_power_of_two"); - fInited = GL_TRUE; - } - return fSupported; -} - -void crServerRedirMuralFBO(CRMuralInfo *mural, GLboolean redir) -{ - if (redir) - { - if (!crServerSupportRedirMuralFBO()) - { - crWarning("FBO not supported, can't redirect window output"); - return; - } - - cr_server.head_spu->dispatch_table.WindowShow(mural->spuWindow, GL_FALSE); - - if (mural->idFBO==0) - { - crServerCreateMuralFBO(mural); - } - - if (!crStateGetCurrent()->framebufferobject.drawFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, mural->idFBO); - } - if (!crStateGetCurrent()->framebufferobject.readFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, mural->idFBO); - } - - if (cr_server.curClient && cr_server.curClient->currentMural == mural) - { - crStateGetCurrent()->buffer.width = 0; - crStateGetCurrent()->buffer.height = 0; - } - } - else - { - cr_server.head_spu->dispatch_table.WindowShow(mural->spuWindow, mural->bVisible); - - if (mural->bUseFBO && crServerSupportRedirMuralFBO()) - { - if (!crStateGetCurrent()->framebufferobject.drawFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); - } - if (!crStateGetCurrent()->framebufferobject.readFB) - { - cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); - } - } - - if (cr_server.curClient && cr_server.curClient->currentMural == mural) - { - crStateGetCurrent()->buffer.width = mural->width; - crStateGetCurrent()->buffer.height = mural->height; - } - } - - mural->bUseFBO = redir; -} - -void crServerCreateMuralFBO(CRMuralInfo *mural) -{ - CRContext *ctx = crStateGetCurrent(); - GLuint uid; - GLenum status; - SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; - - CRASSERT(mural->idFBO==0); - - /*Color texture*/ - gl->GenTextures(1, &mural->idColorTex); - gl->BindTexture(GL_TEXTURE_2D, mural->idColorTex); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) - { - gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - } - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mural->width, mural->height, - 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - - /*Depth&Stencil*/ - gl->GenRenderbuffersEXT(1, &mural->idDepthStencilRB); - gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); - gl->RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, - mural->width, mural->height); - - /*FBO*/ - gl->GenFramebuffersEXT(1, &mural->idFBO); - gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mural->idFBO); - - gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, mural->idColorTex, 0); - gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); - gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); - - status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status!=GL_FRAMEBUFFER_COMPLETE_EXT) - { - crWarning("FBO status(0x%x) isn't complete", status); - } - - mural->fboWidth = mural->width; - mural->fboHeight = mural->height; - - /*PBO*/ - if (cr_server.bUsePBOForReadback) - { - gl->GenBuffersARB(1, &mural->idPBO); - gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, mural->idPBO); - gl->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, mural->width*mural->height*4, 0, GL_STREAM_READ_ARB); - gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); - - if (!mural->idPBO) - { - crWarning("PBO create failed"); - } - } - - /*Restore gl state*/ - uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid; - gl->BindTexture(GL_TEXTURE_2D, uid); - - uid = ctx->framebufferobject.renderbuffer ? ctx->framebufferobject.renderbuffer->hwid:0; - gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, uid); - - uid = ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0; - gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, uid); - - uid = ctx->framebufferobject.readFB ? ctx->framebufferobject.readFB->hwid:0; - gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER, uid); - - if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) - { - gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid); - } -} - -void crServerDeleteMuralFBO(CRMuralInfo *mural) -{ - CRASSERT(!mural->bUseFBO); - - if (mural->idFBO!=0) - { - cr_server.head_spu->dispatch_table.DeleteTextures(1, &mural->idColorTex); - cr_server.head_spu->dispatch_table.DeleteRenderbuffersEXT(1, &mural->idDepthStencilRB); - cr_server.head_spu->dispatch_table.DeleteFramebuffersEXT(1, &mural->idFBO); - - mural->idFBO = 0; - mural->idColorTex = 0; - mural->idDepthStencilRB = 0; - } - - if (mural->idPBO!=0) - { - CRASSERT(cr_server.bUsePBOForReadback); - cr_server.head_spu->dispatch_table.DeleteBuffersARB(1, &mural->idPBO); - mural->idPBO = 0; - } -} - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -static GLboolean crServerIntersectRect(CRrecti *a, CRrecti *b, CRrecti *rect) -{ - CRASSERT(a && b && rect); - - rect->x1 = MAX(a->x1, b->x1); - rect->x2 = MIN(a->x2, b->x2); - rect->y1 = MAX(a->y1, b->y1); - rect->y2 = MIN(a->y2, b->y2); - - return (rect->x2>rect->x1) && (rect->y2>rect->y1); -} - -static GLboolean crServerIntersectScreen(CRMuralInfo *mural, int sId, CRrecti *rect) -{ - rect->x1 = MAX(mural->gX, cr_server.screen[sId].x); - rect->x2 = MIN(mural->gX+(int)mural->fboWidth, cr_server.screen[sId].x+(int)cr_server.screen[sId].w); - rect->y1 = MAX(mural->gY, cr_server.screen[sId].y); - rect->y2 = MIN(mural->gY+(int)mural->fboHeight, cr_server.screen[sId].y+(int)cr_server.screen[sId].h); - - return (rect->x2>rect->x1) && (rect->y2>rect->y1); -} - -static void crServerCopySubImage(char *pDst, char* pSrc, CRrecti *pRect, int srcWidth, int srcHeight) -{ - int i; - int dstrowsize = 4*(pRect->x2-pRect->x1); - int srcrowsize = 4*srcWidth; - int height = pRect->y2-pRect->y1; - - pSrc += 4*pRect->x1 + srcrowsize*(srcHeight-1-pRect->y1); - - for (i=0; i<height; ++i) - { - crMemcpy(pDst, pSrc, dstrowsize); - - pSrc -= srcrowsize; - pDst += dstrowsize; - } -} - -static void crServerTransformRect(CRrecti *pDst, CRrecti *pSrc, int dx, int dy) -{ - pDst->x1 = pSrc->x1+dx; - pDst->x2 = pSrc->x2+dx; - pDst->y1 = pSrc->y1+dy; - pDst->y2 = pSrc->y2+dy; -} - -void crServerPresentFBO(CRMuralInfo *mural) -{ - char *pixels=NULL, *tmppixels; - GLuint uid; - int i, j; - CRrecti rect, rectwr, sectr; - GLboolean bUsePBO; - CRContext *ctx = crStateGetCurrent(); - - CRASSERT(cr_server.pfnPresentFBO); - - if (!mural->bVisible) - { - return; - } - - if (!mural->width || !mural->height) - { - return; - } - - if (cr_server.bUsePBOForReadback && !mural->idPBO) - { - crWarning("Mural doesn't have PBO even though bUsePBOForReadback is set!"); - } - - bUsePBO = cr_server.bUsePBOForReadback && mural->idPBO; - - cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, mural->idColorTex); - - if (bUsePBO) - { - CRASSERT(mural->idPBO); - cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, mural->idPBO); - } - else - { - if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) - { - cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); - } - - pixels = crAlloc(4*mural->fboWidth*mural->fboHeight); - if (!pixels) - { - crWarning("Out of memory in crServerPresentFBO"); - return; - } - } - - /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/ - cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels); - - /*restore gl state*/ - uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid; - cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, uid); - - if (bUsePBO) - { - pixels = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); - if (!pixels) - { - crWarning("Failed to MapBuffer in crServerPresentFBO"); - return; - } - } - - for (i=0; i<cr_server.screenCount; ++i) - { - if (crServerIntersectScreen(mural, i, &rect)) - { - /* rect in window relative coords */ - crServerTransformRect(&rectwr, &rect, -mural->gX, -mural->gY); - - if (!mural->pVisibleRects) - { - /*we don't get any rects info for guest compiz windows, so we treat windows as visible unless explicitly received 0 visible rects*/ - if (!mural->bReceivedRects) - { - tmppixels = crAlloc(4*(rect.x2-rect.x1)*(rect.y2-rect.y1)); - if (!tmppixels) - { - crWarning("Out of memory in crServerPresentFBO"); - crFree(pixels); - return; - } - - crServerCopySubImage(tmppixels, pixels, &rectwr, mural->fboWidth, mural->fboHeight); - /*Note: pfnPresentFBO would free tmppixels*/ - cr_server.pfnPresentFBO(tmppixels, i, rect.x1-cr_server.screen[i].x, rect.y1-cr_server.screen[i].y, rect.x2-rect.x1, rect.y2-rect.y1); - } - } - else - { - for (j=0; j<mural->cVisibleRects; ++j) - { - if (crServerIntersectRect(&rectwr, (CRrecti*) &mural->pVisibleRects[4*j], §r)) - { - tmppixels = crAlloc(4*(sectr.x2-sectr.x1)*(sectr.y2-sectr.y1)); - if (!tmppixels) - { - crWarning("Out of memory in crServerPresentFBO"); - crFree(pixels); - return; - } - - crServerCopySubImage(tmppixels, pixels, §r, mural->fboWidth, mural->fboHeight); - /*Note: pfnPresentFBO would free tmppixels*/ - cr_server.pfnPresentFBO(tmppixels, i, - sectr.x1+mural->gX-cr_server.screen[i].x, - sectr.y1+mural->gY-cr_server.screen[i].y, - sectr.x2-sectr.x1, sectr.y2-sectr.y1); - } - } - } - } - } - - if (mural->pvOutputRedirectInstance) - { - /* @todo find out why presentfbo is not called but crorframe is called. */ - cr_server.outputRedirect.CRORFrame(mural->pvOutputRedirectInstance, - pixels, - 4 * mural->fboWidth * mural->fboHeight); - } - - if (bUsePBO) - { - cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); - cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); - } - else - { - crFree(pixels); - if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) - { - cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); - } - } -} - -GLboolean crServerIsRedirectedToFBO() -{ - return cr_server.curClient - && cr_server.curClient->currentMural - && cr_server.curClient->currentMural->bUseFBO; -} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp new file mode 100644 index 00000000..10cdf5cf --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp @@ -0,0 +1,840 @@ +/* $Id: server_muralfbo.cpp $ */ + +/** @file + * VBox crOpenGL: Window to FBO redirect support. + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "server.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_vreg.h" +#include "render/renderspu.h" + +static void crServerRedirMuralFbSync(CRMuralInfo *mural); + +void crServerCheckMuralGeometry(CRMuralInfo *mural) +{ + if (!mural->CreateInfo.externalID) + return; + + CRASSERT(mural->spuWindow); + CRASSERT(mural->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID); + + if (!mural->width || !mural->height + || mural->fboWidth != mural->width + || mural->fboHeight != mural->height) + { + crServerRedirMuralFbClear(mural); + crServerRedirMuralFBO(mural, false); + crServerDeleteMuralFBO(mural); + } + + if (!mural->width || !mural->height) + return; + + crServerRedirMuralFBO(mural, true); + crServerRedirMuralFbSync(mural); +} + +static void crServerCheckMuralGeometryCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + + if (!pMI->fRedirected || pMI == data2) + return; + + crServerCheckMuralGeometry(pMI); +} + + +void crServerCheckAllMuralGeometry(CRMuralInfo *pMI) +{ + CR_FBMAP Map; + int rc = CrPMgrHlpGlblUpdateBegin(&Map); + if (!RT_SUCCESS(rc)) + { + WARN(("CrPMgrHlpGlblUpdateBegin failed %d", rc)); + return; + } + + crHashtableWalk(cr_server.muralTable, crServerCheckMuralGeometryCB, pMI); + + if (pMI) + crServerCheckMuralGeometry(pMI); + + CrPMgrHlpGlblUpdateEnd(&Map); +} + +GLboolean crServerSupportRedirMuralFBO(void) +{ + static GLboolean fInited = GL_FALSE; + static GLboolean fSupported = GL_FALSE; + if (!fInited) + { + const GLubyte* pExt = cr_server.head_spu->dispatch_table.GetString(GL_REAL_EXTENSIONS); + + fSupported = ( NULL!=crStrstr((const char*)pExt, "GL_ARB_framebuffer_object") + || NULL!=crStrstr((const char*)pExt, "GL_EXT_framebuffer_object")) + && NULL!=crStrstr((const char*)pExt, "GL_ARB_texture_non_power_of_two"); + fInited = GL_TRUE; + } + return fSupported; +} + +static void crServerCreateMuralFBO(CRMuralInfo *mural); + +void crServerRedirMuralFbClear(CRMuralInfo *mural) +{ + uint32_t i; + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + CrFbUpdateEnd(pData->hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } + mural->cUsedFBDatas = 0; + + for (i = 0; i < cr_server.screenCount; ++i) + { + GLuint j; + CR_FBDATA *pData = &mural->aFBDatas[i]; + if (!pData->hFb) + continue; + + CrFbEntryRelease(pData->hFb, pData->hFbEntry); + pData->hFbEntry = NULL; + + for (j = 0; j < mural->cBuffers; ++j) + { + CrTdRelease(pData->apTexDatas[j]); + pData->apTexDatas[j] = NULL; + } + + pData->hFb = NULL; + } +} + +static int crServerRedirMuralDbSyncFb(CRMuralInfo *mural, HCR_FRAMEBUFFER hFb, CR_FBDATA **ppData) +{ + CR_FBDATA *pData; + const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(hFb); + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb); + RTRECT FbRect = *CrVrScrCompositorRectGet(pCompositor); + RTRECT DefaultRegionsRect; + const RTRECT * pRegions; + uint32_t cRegions; + RTPOINT Pos; + RTRECT MuralRect; + int rc; + + CRASSERT(mural->fRedirected); + + *ppData = NULL; + + if (!mural->bVisible) + return VINF_SUCCESS; + + MuralRect.xLeft = mural->gX; + MuralRect.yTop = mural->gY; + MuralRect.xRight = MuralRect.xLeft + mural->width; + MuralRect.yBottom = MuralRect.yTop + mural->height; + + Pos.x = mural->gX - pScreenInfo->i32OriginX; + Pos.y = mural->gY - pScreenInfo->i32OriginY; + + VBoxRectTranslate(&FbRect, pScreenInfo->i32OriginX, pScreenInfo->i32OriginY); + + VBoxRectIntersect(&FbRect, &MuralRect); + + if (VBoxRectIsZero(&FbRect)) + return VINF_SUCCESS; + + if (mural->bReceivedRects) + { + pRegions = (const RTRECT*)mural->pVisibleRects; + cRegions = mural->cVisibleRects; + } + else + { + DefaultRegionsRect.xLeft = 0; + DefaultRegionsRect.yTop = 0; + DefaultRegionsRect.xRight = mural->width; + DefaultRegionsRect.yBottom = mural->height; + pRegions = &DefaultRegionsRect; + cRegions = 1; + } + + if (!cRegions) + return VINF_SUCCESS; + + pData = &mural->aFBDatas[pScreenInfo->u32ViewIndex]; + + if (!pData->hFb) + { + pData->hFb = hFb; + + for (uint32_t i = 0; i < mural->cBuffers; ++i) + { + VBOXVR_TEXTURE Tex; + Tex.width = mural->width; + Tex.height = mural->height; + Tex.hwid = mural->aidColorTexs[i]; + Tex.target = GL_TEXTURE_2D; + + pData->apTexDatas[i] = CrFbTexDataCreate(&Tex); + } + + rc = CrFbEntryCreateForTexData(hFb, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)], 0, &pData->hFbEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexData failed rc %d", rc)); + } + } + else + { + CRASSERT(pData->hFb == hFb); + } + + rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbUpdateBegin failed rc %d", rc)); + return rc; + } + + rc = CrFbEntryRegionsSet(hFb, pData->hFbEntry, &Pos, cRegions, pRegions, true); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryRegionsSet failed rc %d", rc)); + } + + CrFbUpdateEnd(hFb); + + const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry = CrFbEntryGetCompositorEntry(pData->hFbEntry); + if (CrVrScrCompositorEntryIsUsed(pCEntry)) + *ppData = pData; + + return rc; +} + +static void crServerRedirMuralFbSync(CRMuralInfo *mural) +{ + uint32_t i; + uint32_t cUsedFBs = 0; + HCR_FRAMEBUFFER ahUsedFbs[CR_MAX_GUEST_MONITORS]; + HCR_FRAMEBUFFER hFb; + + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + ahUsedFbs[cUsedFBs] = pData->hFb; + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + ++cUsedFBs; + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } + mural->cUsedFBDatas = 0; + + if (!mural->width + || !mural->height + || !mural->bVisible + ) + goto end; + + CRASSERT(mural->fRedirected); + + for (hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + CR_FBDATA *pData = NULL; + int rc = crServerRedirMuralDbSyncFb(mural, hFb, &pData); + if (!RT_SUCCESS(rc)) + { + WARN(("crServerRedirMuralDbSyncFb failed %d", rc)); + continue; + } + + if (!pData) + continue; + + mural->apUsedFBDatas[mural->cUsedFBDatas] = pData; + ++mural->cUsedFBDatas; + } + +end: + + for (i = 0; i < cUsedFBs; ++i) + { + CrFbUpdateEnd(ahUsedFbs[i]); + } +} + +static void crVBoxServerMuralFbCleanCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2; + uint32_t i; + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + if (hFb != pData->hFb) + continue; + + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + break; + } +} + +static void crVBoxServerMuralFbSetCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2; + uint32_t i; + CR_FBDATA *pData = NULL; + bool fFbWasUsed = false; + + Assert(hFb); + + if (!pMI->fRedirected) + { + Assert(!pMI->cUsedFBDatas); + return; + } + + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + if (hFb != pData->hFb) + continue; + + fFbWasUsed = true; + break; + } + + if (CrFbIsEnabled(hFb)) + { + int rc = crServerRedirMuralDbSyncFb(pMI, hFb, &pData); + if (!RT_SUCCESS(rc)) + { + WARN(("crServerRedirMuralDbSyncFb failed %d", rc)); + pData = NULL; + } + } + + if (pData) + { + if (!fFbWasUsed) + { + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + uint32_t idCurScreen = CrFbGetScreenInfo(pData->hFb)->u32ViewIndex; + if (idCurScreen > idScreen) + break; + + Assert(idCurScreen != idScreen); + } + + for (int j = pMI->cUsedFBDatas; j > i; --j) + { + pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j-1]; + } + + pMI->apUsedFBDatas[i] = pData; + ++pMI->cUsedFBDatas; + } + /* else - nothing to do */ + } + else + { + if (fFbWasUsed) + { + for (int j = i; j < pMI->cUsedFBDatas - 1; ++j) + { + pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j+1]; + } + --pMI->cUsedFBDatas; + } + /* else - nothing to do */ + } +} + +void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb) +{ + crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbSetCB, hFb); +} + +void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb) +{ + crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbCleanCB, hFb); +} + + +static int crVBoxServerResizeScreen(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM) +{ + int rc; + HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pScreen->u32ViewIndex); + if (!hFb) + { + WARN(("CrPMgrFbGet failed")); + return VERR_INVALID_PARAMETER; + } + + rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbUpdateBegin failed %d", rc)); + return rc; + } + + crVBoxServerMuralFbResizeBegin(hFb); + + rc = CrFbResize(hFb, pScreen, pvVRAM); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbResize failed %d", rc)); + } + + crVBoxServerMuralFbResizeEnd(hFb); + + CrFbUpdateEnd(hFb); + + CrPMgrNotifyResize(hFb); + + return rc; +} + +DECLEXPORT(int) crVBoxServerNotifyResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM) +{ + int rc = crVBoxServerResizeScreen(pScreen, pvVRAM); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; +} + +void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled) +{ + if (!mural->fRedirected == !fEnabled) + { + return; + } + + if (!mural->CreateInfo.externalID) + { + WARN(("trying to change redir setting for internal mural %d", mural->spuWindow)); + return; + } + + if (fEnabled) + { + if (!crServerSupportRedirMuralFBO()) + { + WARN(("FBO not supported, can't redirect window output")); + return; + } + + if (mural->aidFBOs[0]==0) + { + crServerCreateMuralFBO(mural); + } + + if (cr_server.curClient && cr_server.curClient->currentMural == mural) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + } + + crStateGetCurrent()->buffer.width = 0; + crStateGetCurrent()->buffer.height = 0; + } + } + else + { + if (cr_server.curClient && cr_server.curClient->currentMural == mural) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); + } + + crStateGetCurrent()->buffer.width = mural->width; + crStateGetCurrent()->buffer.height = mural->height; + } + } + + mural->fRedirected = !!fEnabled; +} + +static void crServerCreateMuralFBO(CRMuralInfo *mural) +{ + CRContext *ctx = crStateGetCurrent(); + GLuint uid, i; + GLenum status; + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + CRContextInfo *pMuralContextInfo; + + CRASSERT(mural->aidFBOs[0]==0); + CRASSERT(mural->aidFBOs[1]==0); + + pMuralContextInfo = cr_server.currentCtxInfo; + if (!pMuralContextInfo) + { + /* happens on saved state load */ + CRASSERT(cr_server.MainContextInfo.SpuContext); + pMuralContextInfo = &cr_server.MainContextInfo; + cr_server.head_spu->dispatch_table.MakeCurrent(mural->spuWindow, 0, cr_server.MainContextInfo.SpuContext); + } + + if (pMuralContextInfo->CreateInfo.realVisualBits != mural->CreateInfo.realVisualBits) + { + WARN(("mural visual bits do not match with current context visual bits!")); + } + + mural->cBuffers = 2; + mural->iBbBuffer = 0; + /*Color texture*/ + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } + + for (i = 0; i < mural->cBuffers; ++i) + { + gl->GenTextures(1, &mural->aidColorTexs[i]); + gl->BindTexture(GL_TEXTURE_2D, mural->aidColorTexs[i]); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mural->width, mural->height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + + /*Depth&Stencil*/ + gl->GenRenderbuffersEXT(1, &mural->idDepthStencilRB); + gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + gl->RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, + mural->width, mural->height); + + /*FBO*/ + for (i = 0; i < mural->cBuffers; ++i) + { + gl->GenFramebuffersEXT(1, &mural->aidFBOs[i]); + gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mural->aidFBOs[i]); + + gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, mural->aidColorTexs[i], 0); + gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + + status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status!=GL_FRAMEBUFFER_COMPLETE_EXT) + { + WARN(("FBO status(0x%x) isn't complete", status)); + } + } + + mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + + mural->fboWidth = mural->width; + mural->fboHeight = mural->height; + + mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + + /*Restore gl state*/ + uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid; + gl->BindTexture(GL_TEXTURE_2D, uid); + + uid = ctx->framebufferobject.renderbuffer ? ctx->framebufferobject.renderbuffer->hwid:0; + gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, uid); + + uid = ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0; + gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, uid); + + uid = ctx->framebufferobject.readFB ? ctx->framebufferobject.readFB->hwid:0; + gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER, uid); + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid); + } + + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); + } + else + { + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + + CRASSERT(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]); +} + +void crServerDeleteMuralFBO(CRMuralInfo *mural) +{ + if (mural->aidFBOs[0]!=0) + { + GLuint i; + for (i = 0; i < mural->cBuffers; ++i) + { + cr_server.head_spu->dispatch_table.DeleteTextures(1, &mural->aidColorTexs[i]); + mural->aidColorTexs[i] = 0; + } + + cr_server.head_spu->dispatch_table.DeleteRenderbuffersEXT(1, &mural->idDepthStencilRB); + mural->idDepthStencilRB = 0; + + for (i = 0; i < mural->cBuffers; ++i) + { + cr_server.head_spu->dispatch_table.DeleteFramebuffersEXT(1, &mural->aidFBOs[i]); + mural->aidFBOs[i] = 0; + } + } + + mural->cBuffers = 0; +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static GLboolean crServerIntersectRect(CRrecti *a, CRrecti *b, CRrecti *rect) +{ + CRASSERT(a && b && rect); + + rect->x1 = MAX(a->x1, b->x1); + rect->x2 = MIN(a->x2, b->x2); + rect->y1 = MAX(a->y1, b->y1); + rect->y2 = MIN(a->y2, b->y2); + + return (rect->x2>rect->x1) && (rect->y2>rect->y1); +} + +DECLEXPORT(void) crServerVBoxCompositionSetEnableStateGlobal(GLboolean fEnable) +{ +} + +DECLEXPORT(void) crServerVBoxScreenshotRelease(CR_SCREENSHOT *pScreenshot) +{ + if (pScreenshot->fDataAllocated) + { + RTMemFree(pScreenshot->Img.pvData); + pScreenshot->fDataAllocated = 0; + } +} + +DECLEXPORT(int) crServerVBoxScreenshotGet(uint32_t u32Screen, uint32_t width, uint32_t height, uint32_t pitch, void *pvBuffer, CR_SCREENSHOT *pScreenshot) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32Screen); + if (!hFb) + return VERR_INVALID_STATE; + + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + + if (!width) + width = pScreen->u32Width; + if (!height) + height = pScreen->u32Height; + if (!pitch) + pitch = pScreen->u32LineSize; + + if (CrFbHas3DData(hFb) + || pScreen->u32Width != width + || pScreen->u32Height != height + || pScreen->u32LineSize != pitch + || pScreen->u16BitsPerPixel != 32) + { + RTRECT SrcRect; + RTRECT DstRect; + + pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height; + if (!pvBuffer) + { + pScreenshot->Img.pvData = RTMemAlloc(pScreenshot->Img.cbData); + if (!pScreenshot->Img.pvData) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + pScreenshot->fDataAllocated = 1; + } + else + { + pScreenshot->Img.pvData = pvBuffer; + pScreenshot->fDataAllocated = 0; + } + + pScreenshot->Img.enmFormat = GL_BGRA; + pScreenshot->Img.width = width; + pScreenshot->Img.height = height; + pScreenshot->Img.bpp = 32; + pScreenshot->Img.pitch = pitch; + SrcRect.xLeft = 0; + SrcRect.yTop = 0; + SrcRect.xRight = pScreen->u32Width; + SrcRect.yBottom = pScreen->u32Height; + DstRect.xLeft = 0; + DstRect.yTop = 0; + DstRect.xRight = width; + DstRect.yBottom = height; + int rc = CrFbBltGetContents(hFb, &SrcRect, &DstRect, 1, &DstRect, &pScreenshot->Img); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbBltGetContents failed %d", rc)); + crServerVBoxScreenshotRelease(pScreenshot); + return rc; + } + } + else + { + pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height; + if (!pvBuffer) + pScreenshot->Img.pvData = CrFbGetVRAM(hFb); + else + { + pScreenshot->Img.pvData = pvBuffer; + memcpy(pvBuffer, CrFbGetVRAM(hFb), pScreenshot->Img.cbData); + } + pScreenshot->Img.enmFormat = GL_BGRA; + pScreenshot->Img.width = pScreen->u32Width; + pScreenshot->Img.height = pScreen->u32Height; + pScreenshot->Img.bpp = pScreen->u16BitsPerPixel; + pScreenshot->Img.pitch = pScreen->u32LineSize; + + pScreenshot->fDataAllocated = 0; + } + + pScreenshot->u32Screen = u32Screen; + + return VINF_SUCCESS; +} + +extern DECLEXPORT(int) crServerVBoxWindowsShow(bool fShow) +{ + return CrPMgrModeWinVisible(fShow); +} + +void crServerPresentFBO(CRMuralInfo *mural) +{ + uint32_t i; + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + CrFbEntryTexDataUpdate(pData->hFb, pData->hFbEntry, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)]); + CrFbUpdateEnd(pData->hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } +} + +GLboolean crServerIsRedirectedToFBO() +{ +#ifdef DEBUG_misha + Assert(cr_server.curClient); + if (cr_server.curClient) + { + Assert(cr_server.curClient->currentMural == cr_server.currentMural); + Assert(cr_server.curClient->currentCtxInfo == cr_server.currentCtxInfo); + } +#endif + return cr_server.curClient + && cr_server.curClient->currentMural + && cr_server.curClient->currentMural->fRedirected; +} + +GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer) +{ + switch (buffer) + { + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + return CR_SERVER_FBO_FB_IDX(mural); + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + return CR_SERVER_FBO_BB_IDX(mural); + case GL_NONE: + case GL_AUX0: + case GL_AUX1: + case GL_AUX2: + case GL_AUX3: + case GL_LEFT: + case GL_RIGHT: + case GL_FRONT_AND_BACK: + return -1; + default: + WARN(("crServerMuralFBOIdxFromBufferName: invalid buffer passed 0x%x", buffer)); + return -2; + } +} + +void crServerMuralFBOSwapBuffers(CRMuralInfo *mural) +{ + CRContext *ctx = crStateGetCurrent(); + GLuint iOldCurDrawBuffer = mural->iCurDrawBuffer; + GLuint iOldCurReadBuffer = mural->iCurReadBuffer; + mural->iBbBuffer = ((mural->iBbBuffer + 1) % (mural->cBuffers)); + if (mural->iCurDrawBuffer >= 0) + mural->iCurDrawBuffer = ((mural->iCurDrawBuffer + 1) % (mural->cBuffers)); + if (mural->iCurReadBuffer >= 0) + mural->iCurReadBuffer = ((mural->iCurReadBuffer + 1) % (mural->cBuffers)); + Assert(iOldCurDrawBuffer != mural->iCurDrawBuffer || mural->cBuffers == 1 || mural->iCurDrawBuffer < 0); + Assert(iOldCurReadBuffer != mural->iCurReadBuffer || mural->cBuffers == 1 || mural->iCurReadBuffer < 0); + if (!ctx->framebufferobject.drawFB && iOldCurDrawBuffer != mural->iCurDrawBuffer) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + if (!ctx->framebufferobject.readFB && iOldCurReadBuffer != mural->iCurReadBuffer) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + } + Assert(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_presenter.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_presenter.cpp new file mode 100644 index 00000000..e1dca828 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_presenter.cpp @@ -0,0 +1,4787 @@ +/* $Id: server_presenter.cpp $ */ + +/** @file + * Presenter API + */ + +/* + * Copyright (C) 2012-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_net.h" +#include "cr_rand.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_mem.h" +#include "cr_string.h" +#include <cr_vreg.h> +#include <cr_htable.h> +#include <cr_bmpscale.h> + +#include <iprt/cdefs.h> +#include <iprt/types.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/list.h> + + +#ifdef DEBUG_misha +# define VBOXVDBG_MEMCACHE_DISABLE +#endif + +#ifndef VBOXVDBG_MEMCACHE_DISABLE +# include <iprt/memcache.h> +#endif + +#include "render/renderspu.h" + +class ICrFbDisplay +{ +public: + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) = 0; + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) = 0; + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual ~ICrFbDisplay() {} +}; + +class CrFbDisplayComposite; +class CrFbDisplayBase; +class CrFbDisplayWindow; +class CrFbDisplayWindowRootVr; +class CrFbDisplayVrdp; + +typedef struct CR_FRAMEBUFFER +{ + VBOXVR_SCR_COMPOSITOR Compositor; + struct VBVAINFOSCREEN ScreenInfo; + void *pvVram; + ICrFbDisplay *pDisplay; + RTLISTNODE EntriesList; + uint32_t cEntries; /* <- just for debugging */ + uint32_t cUpdating; + CRHTABLE SlotTable; +} CR_FRAMEBUFFER; + +typedef union CR_FBENTRY_FLAGS +{ + struct { + uint32_t fCreateNotified : 1; + uint32_t fInList : 1; + uint32_t Reserved : 30; + }; + uint32_t Value; +} CR_FBENTRY_FLAGS; + +typedef struct CR_FRAMEBUFFER_ENTRY +{ + VBOXVR_SCR_COMPOSITOR_ENTRY Entry; + RTLISTNODE Node; + uint32_t cRefs; + CR_FBENTRY_FLAGS Flags; + CRHTABLE HTable; +} CR_FRAMEBUFFER_ENTRY; + +typedef struct CR_FBTEX +{ + CR_TEXDATA Tex; + CRTextureObj *pTobj; +} CR_FBTEX; + +#define PCR_FBTEX_FROM_TEX(_pTex) ((CR_FBTEX*)((uint8_t*)(_pTex) - RT_OFFSETOF(CR_FBTEX, Tex))) +#define PCR_FRAMEBUFFER_FROM_COMPOSITOR(_pCompositor) ((CR_FRAMEBUFFER*)((uint8_t*)(_pCompositor) - RT_OFFSETOF(CR_FRAMEBUFFER, Compositor))) +#define PCR_FBENTRY_FROM_ENTRY(_pEntry) ((CR_FRAMEBUFFER_ENTRY*)((uint8_t*)(_pEntry) - RT_OFFSETOF(CR_FRAMEBUFFER_ENTRY, Entry))) + + +typedef struct CR_FBDISPLAY_INFO +{ + uint32_t u32Mode; + CrFbDisplayWindow *pDpWin; + CrFbDisplayWindowRootVr *pDpWinRootVr; + CrFbDisplayVrdp *pDpVrdp; + CrFbDisplayComposite *pDpComposite; +} CR_FBDISPLAY_INFO; + +typedef struct CR_PRESENTER_GLOBALS +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMEMCACHE FbEntryLookasideList; + RTMEMCACHE FbTexLookasideList; + RTMEMCACHE CEntryLookasideList; +#endif + uint32_t u32DisplayMode; + CRHashTable *pFbTexMap; + CR_FBDISPLAY_INFO aDisplayInfos[CR_MAX_GUEST_MONITORS]; + CR_FBMAP FramebufferInitMap; + CR_FRAMEBUFFER aFramebuffers[CR_MAX_GUEST_MONITORS]; + bool fWindowsForceHidden; + uint32_t cbTmpBuf; + void *pvTmpBuf; + uint32_t cbTmpBuf2; + void *pvTmpBuf2; +} CR_PRESENTER_GLOBALS; + +static CR_PRESENTER_GLOBALS g_CrPresenter; + +/* FRAMEBUFFER */ + +void CrFbInit(CR_FRAMEBUFFER *pFb, uint32_t idScreen) +{ + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = 1; + Rect.yBottom = 1; + memset(pFb, 0, sizeof (*pFb)); + pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED; + pFb->ScreenInfo.u32ViewIndex = idScreen; + CrVrScrCompositorInit(&pFb->Compositor, &Rect); + RTListInit(&pFb->EntriesList); + CrHTableCreate(&pFb->SlotTable, 0); +} + +bool CrFbIsEnabled(CR_FRAMEBUFFER *pFb) +{ + return !(pFb->ScreenInfo.u16Flags & VBVA_SCREEN_F_DISABLED); +} + +HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry); + +const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(CR_FRAMEBUFFER *pFb) +{ + return &pFb->Compositor; +} + +DECLINLINE(CR_FRAMEBUFFER*) CrFbFromCompositor(const struct VBOXVR_SCR_COMPOSITOR* pCompositor) +{ + return RT_FROM_MEMBER(pCompositor, CR_FRAMEBUFFER, Compositor); +} + +const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb) +{ + return &hFb->ScreenInfo; +} + +void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb) +{ + return hFb->pvVram; +} + +int CrFbUpdateBegin(CR_FRAMEBUFFER *pFb) +{ + ++pFb->cUpdating; + + if (pFb->cUpdating == 1) + { + if (pFb->pDisplay) + pFb->pDisplay->UpdateBegin(pFb); + } + + return VINF_SUCCESS; +} + +void CrFbUpdateEnd(CR_FRAMEBUFFER *pFb) +{ + if (!pFb->cUpdating) + { + WARN(("invalid UpdateEnd call!")); + return; + } + + --pFb->cUpdating; + + if (!pFb->cUpdating) + { + if (pFb->pDisplay) + pFb->pDisplay->UpdateEnd(pFb); + } +} + +bool CrFbIsUpdating(const CR_FRAMEBUFFER *pFb) +{ + return !!pFb->cUpdating; +} + +bool CrFbHas3DData(HCR_FRAMEBUFFER hFb) +{ + return !CrVrScrCompositorIsEmpty(&hFb->Compositor); +} + +static void crFbBltMem(uint8_t *pu8Src, int32_t cbSrcPitch, uint8_t *pu8Dst, int32_t cbDstPitch, uint32_t width, uint32_t height) +{ + uint32_t cbCopyRow = width * 4; + + for (uint32_t i = 0; i < height; ++i) + { + memcpy(pu8Dst, pu8Src, cbCopyRow); + + pu8Src += cbSrcPitch; + pu8Dst += cbDstPitch; + } +} + +static void crFbBltImg(const CR_BLITTER_IMG *pSrc, const RTPOINT *pSrcDataPoint, bool fSrcInvert, const RTRECT *pCopyRect, const RTPOINT *pDstDataPoint, CR_BLITTER_IMG *pDst) +{ + int32_t srcX = pCopyRect->xLeft - pSrcDataPoint->x; + int32_t srcY = pCopyRect->yTop - pSrcDataPoint->y; + Assert(srcX >= 0); + Assert(srcY >= 0); + Assert(srcX < (int32_t)pSrc->width); + Assert(srcY < (int32_t)pSrc->height); + + int32_t dstX = pCopyRect->xLeft - pDstDataPoint->x; + int32_t dstY = pCopyRect->yTop - pDstDataPoint->y; + Assert(dstX >= 0); + Assert(dstY >= 0); + + uint8_t *pu8Src = ((uint8_t*)pSrc->pvData) + pSrc->pitch * (!fSrcInvert ? srcY : pSrc->height - srcY - 1) + srcX * 4; + uint8_t *pu8Dst = ((uint8_t*)pDst->pvData) + pDst->pitch * dstY + dstX * 4; + + crFbBltMem(pu8Src, fSrcInvert ? -((int32_t)pSrc->pitch) : (int32_t)pSrc->pitch, pu8Dst, pDst->pitch, pCopyRect->xRight - pCopyRect->xLeft, pCopyRect->yBottom - pCopyRect->yTop); +} + +static void crFbBltImgScaled(const CR_BLITTER_IMG *pSrc, const RTPOINT *pSrcDataPoint, bool fSrcInvert, const RTRECT *pCopyRect, const RTPOINT *pDstDataPoint, float strX, float strY, CR_BLITTER_IMG *pDst) +{ + int32_t srcX = pCopyRect->xLeft - pSrcDataPoint->x; + int32_t srcY = pCopyRect->yTop - pSrcDataPoint->y; + Assert(srcX >= 0); + Assert(srcY >= 0); + Assert(srcX < (int32_t)pSrc->width); + Assert(srcY < (int32_t)pSrc->height); + + RTPOINT ScaledDtsDataPoint; + RTRECT ScaledCopyRect; + + VBoxRectScaled(pCopyRect, strX, strY, &ScaledCopyRect); + ScaledDtsDataPoint.x = CR_FLOAT_RCAST(int32_t, strX * pDstDataPoint->x); + ScaledDtsDataPoint.y = CR_FLOAT_RCAST(int32_t, strY * pDstDataPoint->y); + + int32_t dstX = ScaledCopyRect.xLeft - ScaledDtsDataPoint.x; + int32_t dstY = ScaledCopyRect.yTop - ScaledDtsDataPoint.y; + Assert(dstX >= 0); + Assert(dstY >= 0); + + int32_t ScaledDstWidth = ScaledCopyRect.xRight - ScaledCopyRect.xLeft; + int32_t delta = (int32_t)pDst->width - dstX - ScaledDstWidth; + if (delta < 0) + ScaledDstWidth += delta; + + if (ScaledDstWidth <= 0) + { + LOG(("ScaledDstWidth <= 0")); + if (ScaledDstWidth < 0) + WARN(("dst width (%d) < 0", ScaledDstWidth)); + return; + } + + int32_t ScaledDstHeight = ScaledCopyRect.yBottom - ScaledCopyRect.yTop; + delta = (int32_t)pDst->height - dstY - ScaledDstHeight; + if (delta < 0) + ScaledDstHeight += delta; + + if (ScaledDstHeight <= 0) + { + LOG(("ScaledDstHeight <= 0")); + if (ScaledDstHeight < 0) + WARN(("dst height (%d) < 0", ScaledDstHeight)); + return; + } + + uint8_t *pu8Src = ((uint8_t*)pSrc->pvData) + pSrc->pitch * (!fSrcInvert ? srcY : pSrc->height - srcY - 1) + srcX * 4; + uint8_t *pu8Dst = ((uint8_t*)pDst->pvData) + pDst->pitch * dstY + dstX * 4; + + CrBmpScale32(pu8Dst, pDst->pitch, + ScaledDstWidth, + ScaledDstHeight, + pu8Src, + fSrcInvert ? -((int32_t)pSrc->pitch) : (int32_t)pSrc->pitch, + pCopyRect->xRight - pCopyRect->xLeft, pCopyRect->yBottom - pCopyRect->yTop); +} + +static void crFbBltImgScaledRects(const CR_BLITTER_IMG *pSrc, const RTPOINT *pSrcDataPoint, bool fSrcInvert, const RTRECT *pCopyRect, const RTPOINT *pDstDataPoint, float strX, float strY, CR_BLITTER_IMG *pDst) +{ + int32_t srcX = pCopyRect->xLeft - pSrcDataPoint->x; + int32_t srcY = pCopyRect->yTop - pSrcDataPoint->y; + Assert(srcX >= 0); + Assert(srcY >= 0); + + RTRECT UnscaledCopyRect; + VBoxRectUnscaled(pCopyRect, strX, strY, &UnscaledCopyRect); + + srcX = CR_FLOAT_RCAST(int32_t, srcX / strX); + srcY = CR_FLOAT_RCAST(int32_t, srcY / strY); + + int32_t UnscaledSrcWidth = UnscaledCopyRect.xRight - UnscaledCopyRect.xLeft; + int32_t delta = (int32_t)pSrc->width - srcX - UnscaledSrcWidth; + if (delta < 0) + UnscaledSrcWidth += delta; + + if (UnscaledSrcWidth <= 0) + { + LOG(("UnscaledSrcWidth <= 0")); + if (UnscaledSrcWidth < 0) + WARN(("src width (%d) < 0", UnscaledSrcWidth)); + return; + } + + int32_t UnscaledSrcHeight = UnscaledCopyRect.yBottom - UnscaledCopyRect.yTop; + delta = (int32_t)pSrc->height - srcY - UnscaledSrcHeight; + if (delta < 0) + UnscaledSrcHeight += delta; + + if (UnscaledSrcHeight <= 0) + { + LOG(("UnscaledSrcHeight <= 0")); + if (UnscaledSrcHeight < 0) + WARN(("src height (%d) < 0", UnscaledSrcHeight)); + return; + } + + int32_t dstX = pCopyRect->xLeft - pDstDataPoint->x; + int32_t dstY = pCopyRect->yTop - pDstDataPoint->y; + Assert(dstX >= 0); + Assert(dstY >= 0); + + + uint8_t *pu8Src = ((uint8_t*)pSrc->pvData) + pSrc->pitch * (!fSrcInvert ? srcY : pSrc->height - srcY - 1) + srcX * 4; + uint8_t *pu8Dst = ((uint8_t*)pDst->pvData) + pDst->pitch * dstY + dstX * 4; + + CrBmpScale32(pu8Dst, pDst->pitch, + pCopyRect->xRight - pCopyRect->xLeft, + pCopyRect->yBottom - pCopyRect->yTop, + pu8Src, + fSrcInvert ? -pSrc->pitch : pSrc->pitch, + UnscaledSrcWidth, + UnscaledSrcHeight + ); +} + +static void crFbImgFromScreenVram(const VBVAINFOSCREEN *pScreen, void *pvVram, CR_BLITTER_IMG *pImg) +{ + pImg->pvData = pvVram; + pImg->cbData = pScreen->u32LineSize * pScreen->u32Height; + pImg->enmFormat = GL_BGRA; + pImg->width = pScreen->u32Width; + pImg->height = pScreen->u32Height; + pImg->bpp = pScreen->u16BitsPerPixel; + pImg->pitch = pScreen->u32LineSize; +} + +static void crFbImgFromFb(HCR_FRAMEBUFFER hFb, CR_BLITTER_IMG *pImg) +{ + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + void *pvVram = CrFbGetVRAM(hFb); + crFbImgFromScreenVram(pScreen, pvVram, pImg); +} + +static int crFbBltGetContentsDirect(HCR_FRAMEBUFFER hFb, const RTRECT *pSrcRect, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + VBOXVR_LIST List; + uint32_t c2DRects = 0; + CR_TEXDATA *pEnteredTex = NULL; + PCR_BLITTER pEnteredBlitter = NULL; + uint32_t width = 0, height = 0; + RTPOINT ScaledEntryPoint = {0}; + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + int32_t srcWidth = pSrcRect->xRight - pSrcRect->xLeft; + int32_t srcHeight = pSrcRect->yBottom - pSrcRect->yTop; + int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + + RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop}; + float strX = ((float)dstWidth) / srcWidth; + float strY = ((float)dstHeight) / srcHeight; + bool fScale = (dstWidth != srcWidth || dstHeight != srcHeight); + + const RTPOINT ZeroPoint = {0, 0}; + + VBoxVrListInit(&List); + int rc = VBoxVrListRectsAdd(&List, 1, CrVrScrCompositorRectGet(&hFb->Compositor), NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsAdd failed rc %d", rc)); + goto end; + } + + CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter); + + for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter); + pEntry; + pEntry = CrVrScrCompositorConstIterNext(&Iter)) + { + uint32_t cRegions; + const RTRECT *pRegions; + rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc)); + goto end; + } + + rc = VBoxVrListRectsSubst(&List, cRegions, pRegions, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsSubst failed rc %d", rc)); + goto end; + } + + for (uint32_t j = 0; j < cRegions; ++j) + { + /* rects are in dst coordinates, + * while the pReg is in source coords + * convert */ + const RTRECT * pReg = &pRegions[j]; + RTRECT ScaledReg; + /* scale */ + VBoxRectScaled(pReg, strX, strY, &ScaledReg); + /* translate */ + VBoxRectTranslate(&ScaledReg, pDstRect->xLeft, pDstRect->yTop); + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + + RTRECT Intersection; + VBoxRectIntersected(pRect, &ScaledReg, &Intersection); + if (VBoxRectIsZero(&Intersection)) + continue; + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + const CR_BLITTER_IMG *pSrcImg; + + if (pEnteredTex != pTex) + { + if (!pEnteredBlitter) + { + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + + if (pEnteredTex) + { + CrTdBltLeave(pEnteredTex); + + pEnteredTex = NULL; + + if (pEnteredBlitter != CrTdBlitterGet(pTex)) + { + WARN(("blitters not equal!")); + CrBltLeave(pEnteredBlitter); + + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + } + + rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltEnter failed %d", rc)); + goto end; + } + + pEnteredTex = pTex; + + const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex); + + width = CR_FLOAT_RCAST(uint32_t, strX * pVrTex->width); + height = CR_FLOAT_RCAST(uint32_t, strY * pVrTex->height); + ScaledEntryPoint.x = CR_FLOAT_RCAST(int32_t, strX * CrVrScrCompositorEntryRectGet(pEntry)->xLeft) + pDstRect->xLeft; + ScaledEntryPoint.y = CR_FLOAT_RCAST(int32_t, strY * CrVrScrCompositorEntryRectGet(pEntry)->yTop) + pDstRect->yTop; + } + + rc = CrTdBltDataAcquireScaled(pTex, GL_BGRA, false, width, height, &pSrcImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + goto end; + } + + bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS); + + crFbBltImg(pSrcImg, &ScaledEntryPoint, fInvert, &Intersection, &ZeroPoint, pImg); + + CrTdBltDataReleaseScaled(pTex, pSrcImg); + } + } + } + + c2DRects = VBoxVrListRectsCount(&List); + if (c2DRects) + { + if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2); + if (!g_CrPresenter.pvTmpBuf2) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf2 = 0; + rc = VERR_NO_MEMORY; + goto end; + } + } + + RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2; + + rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsGet failed, rc %d", rc)); + goto end; + } + + const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor); + + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + for (uint32_t j = 0; j < c2DRects; ++j) + { + const RTRECT * p2DRect = &p2DRects[j]; + RTRECT ScaledReg; + /* scale */ + VBoxRectScaled(p2DRect, strX, strY, &ScaledReg); + /* translate */ + VBoxRectTranslate(&ScaledReg, pDstRect->xLeft, pDstRect->yTop); + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + RTRECT Intersection; + + VBoxRectIntersected(pRect, &ScaledReg, &Intersection); + if (VBoxRectIsZero(&Intersection)) + continue; + + if (!fScale) + crFbBltImg(&FbImg, &DstPoint, false, &Intersection, &ZeroPoint, pImg); + else + crFbBltImgScaledRects(&FbImg, &DstPoint, false, &Intersection, &ZeroPoint, strX, strY, pImg); + } + } + } + +end: + + if (pEnteredTex) + CrTdBltLeave(pEnteredTex); + + if (pEnteredBlitter) + CrBltLeave(pEnteredBlitter); + + VBoxVrListClear(&List); + + return rc; +} + +static int crFbBltGetContentsScaleCPU(HCR_FRAMEBUFFER hFb, const RTRECT *pSrcRect, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + int32_t srcWidth = pSrcRect->xRight - pSrcRect->xLeft; + int32_t srcHeight = pSrcRect->yBottom - pSrcRect->yTop; + int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + + RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop}; + float strX = ((float)dstWidth) / srcWidth; + float strY = ((float)dstHeight) / srcHeight; + + RTRECT DstRect; + VBoxRectUnscaled(pDstRect, strX, strY, &DstRect); + DstRect.xRight = DstRect.xLeft + srcWidth; + DstRect.yBottom = DstRect.yTop + srcHeight; + + /* destination is bigger than the source, do 3D data stretching with CPU */ + CR_BLITTER_IMG Img; + Img.cbData = srcWidth * srcHeight * 4; + Img.pvData = RTMemAlloc(Img.cbData); + if (!Img.pvData) + { + WARN(("RTMemAlloc Failed")); + return VERR_NO_MEMORY; + } + Img.enmFormat = pImg->enmFormat; + Img.width = srcWidth; + Img.height = srcHeight; + Img.bpp = pImg->bpp; + Img.pitch = Img.width * 4; + + int rc = CrFbBltGetContents(hFb, pSrcRect, &DstRect, cRects, pRects, &Img); + if (RT_SUCCESS(rc)) + { + CrBmpScale32((uint8_t *)pImg->pvData, + pImg->pitch, + pImg->width, pImg->height, + (const uint8_t *)Img.pvData, + Img.pitch, + Img.width, Img.height); + } + else + WARN(("CrFbBltGetContents failed %d", rc)); + + RTMemFree(Img.pvData); + + return rc; + +} + +int CrFbBltGetContents(HCR_FRAMEBUFFER hFb, const RTRECT *pSrcRect, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + uint32_t srcWidth = pSrcRect->xRight - pSrcRect->xLeft; + uint32_t srcHeight = pSrcRect->yBottom - pSrcRect->yTop; + uint32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + uint32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + if ((srcWidth == dstWidth + && srcHeight == dstHeight) + || !CrFbHas3DData(hFb) + || (srcWidth * srcHeight > dstWidth * dstHeight)) + { + return crFbBltGetContentsDirect(hFb, pSrcRect, pDstRect, cRects, pRects, pImg); + } + + return crFbBltGetContentsScaleCPU(hFb, pSrcRect, pDstRect, cRects, pRects, pImg); +} + +#if 0 +static int crFbBltPutContentsVram(HCR_FRAMEBUFFER hFb, const RTPOINT *pDstPoint, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg, float strX, float strY) +{ + const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor); + const RTPOINT ZeroPoint = {0}; + + uint32_t fbWidth = (pCompRect->xRight - pCompRect->xLeft); + uint32_t fbHeight = pCompRect->yBottom - pCompRect->yTop; + + uint32_t stretchedWidth = CR_FLOAT_RCAST(uint32_t, strX * fbWidth); + uint32_t stretchedHeight = CR_FLOAT_RCAST(uint32_t, strY * fbHeight); + + CR_BLITTER_IMG FbImg; + + bool fScale = fbWidth != stretchedWidth || fbHeight != stretchedHeight; + + crFbImgFromFb(hFb, &FbImg); + + RTRECT Intersection; + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + VBoxRectIntersected(pRect, pCompRect, &Intersection); + + if (VBoxRectIsZero(&Intersection)) + continue; + + if (!fScale) + crFbBltImg(pImg, pDstPoint, false, &Intersection, &ZeroPoint, &FbImg); + else + crFbBltImgScaled(pImg, pDstPoint, false, &Intersection, &ZeroPoint, strX, strY, &FbImg); + } + + return VINF_SUCCESS; +} + +int CrFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop}; + float strX = ((float)pImg->width) / (pDstRect->xRight - pDstRect->xLeft); + float strY = ((float)pImg->height) / (pDstRect->yBottom - pDstRect->yTop); + + int rc = CrFbEntryRegionsAdd(hFb, NULL, const RTPOINT *pPos, cRects, pRects, true) + if (!hFb->cUpdating) + { + WARN(("not updating\n")); + return VERR_INVALID_STATE; + } +} + +int CrFbBltPutContentsNe(HCR_FRAMEBUFFER hFb, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + uint32_t cCompRects; + const RTRECT *pCompRects; + int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cCompRects, NULL, NULL, &pCompRects); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc)); + return rc; + } + + bool fRegChanged = false; + for (uint32_t i = 0; i < cCompRects; ++i) + { + const RTRECT *pCompRect = pCompRects[i]; + for (uint32_t j = 0; j < cRects; ++j) + { + const RTRECT *pRect = pRects[j]; + if (VBoxRectIsIntersect(pCompRect, pRect)) + { + fRegChanged = true; + break; + } + } + } + + if (fRegChanged) + { + rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + rc = CrFbBltPutContents(hFb, pDstRect, cRects, pRects, pImg); + if (!RT_SUCCESS(rc)) + WARN(("CrFbBltPutContents failed rc %d", rc)); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + + return rc; + } + + return crFbBltPutContentsVram(HCR_FRAMEBUFFER hFb, const RTPOINT *pDstPoint, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg, float strX, float strY); + + const RTPOINT ZeroPoint = {0, 0}; + + c2DRects = VBoxVrListRectsCount(&List); + if (c2DRects) + { + if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2); + if (!g_CrPresenter.pvTmpBuf2) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf2 = 0; + rc = VERR_NO_MEMORY; + goto end; + } + } + + RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2; + + rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsGet failed, rc %d", rc)); + goto end; + } + + RTPOINT Pos = {0}; + const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor); + + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + for (uint32_t j = 0; j < c2DRects; ++j) + { + const RTRECT * p2DRect = &p2DRects[j]; + RTRECT Intersection; + VBoxRectIntersected(pRect, p2DRect, &Intersection); + if (VBoxRectIsZero(&Intersection)) + continue; + + if (!fScale) + crFbBltImg(&FbImg, &ZeroPoint, false, &Intersection, &SrcPoint, pImg); + else + crFbBltImgScaled(&FbImg, &ZeroPoint, false, &Intersection, &SrcPoint, strX, strY, pImg); + } + } + } + +end: + + if (pEnteredTex) + CrTdBltLeave(pEnteredTex); + + if (pEnteredBlitter) + CrBltLeave(pEnteredBlitter); + + VBoxVrListClear(&List); + + return rc; +} +#endif + +int CrFbResize(CR_FRAMEBUFFER *pFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM) +{ + if (!pFb->cUpdating) + { + WARN(("no update in progress")); + return VERR_INVALID_STATE; + } + + if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED) + { + CrVrScrCompositorClear(&pFb->Compositor); + } + + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = pScreen->u32Width; + Rect.yBottom = pScreen->u32Height; + int rc = CrVrScrCompositorRectSet(&pFb->Compositor, &Rect, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRectSet failed rc %d", rc)); + return rc; + } + + pFb->ScreenInfo = *pScreen; + pFb->pvVram = pvVRAM; + + if (pFb->pDisplay) + pFb->pDisplay->FramebufferChanged(pFb); + + return VINF_SUCCESS; +} + +void CrFbTerm(CR_FRAMEBUFFER *pFb) +{ + if (pFb->cUpdating) + { + WARN(("update in progress")); + return; + } + uint32_t idScreen = pFb->ScreenInfo.u32ViewIndex; + + CrVrScrCompositorClear(&pFb->Compositor); + CrHTableDestroy(&pFb->SlotTable); + + Assert(RTListIsEmpty(&pFb->EntriesList)); + Assert(!pFb->cEntries); + + memset(pFb, 0, sizeof (*pFb)); + + pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED; + pFb->ScreenInfo.u32ViewIndex = idScreen; +} + +ICrFbDisplay* CrFbDisplayGet(CR_FRAMEBUFFER *pFb) +{ + return pFb->pDisplay; +} + +int CrFbDisplaySet(CR_FRAMEBUFFER *pFb, ICrFbDisplay *pDisplay) +{ + if (pFb->cUpdating) + { + WARN(("update in progress")); + return VERR_INVALID_STATE; + } + + if (pFb->pDisplay == pDisplay) + return VINF_SUCCESS; + + pFb->pDisplay = pDisplay; + + return VINF_SUCCESS; +} + +#define CR_PMGR_MODE_WINDOW 0x1 +/* mutually exclusive with CR_PMGR_MODE_WINDOW */ +#define CR_PMGR_MODE_ROOTVR 0x2 +#define CR_PMGR_MODE_VRDP 0x4 +#define CR_PMGR_MODE_ALL 0x7 + +static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove); + +static CR_FBTEX* crFbTexAlloc() +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (CR_FBTEX*)RTMemCacheAlloc(g_CrPresenter.FbTexLookasideList); +#else + return (CR_FBTEX*)RTMemAlloc(sizeof (CR_FBTEX)); +#endif +} + +static void crFbTexFree(CR_FBTEX *pTex) +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.FbTexLookasideList, pTex); +#else + RTMemFree(pTex); +#endif +} + +static CR_FRAMEBUFFER_ENTRY* crFbEntryAlloc() +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (CR_FRAMEBUFFER_ENTRY*)RTMemCacheAlloc(g_CrPresenter.FbEntryLookasideList); +#else + return (CR_FRAMEBUFFER_ENTRY*)RTMemAlloc(sizeof (CR_FRAMEBUFFER_ENTRY)); +#endif +} + +static void crFbEntryFree(CR_FRAMEBUFFER_ENTRY *pEntry) +{ + Assert(!CrVrScrCompositorEntryIsUsed(&pEntry->Entry)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.FbEntryLookasideList, pEntry); +#else + RTMemFree(pEntry); +#endif +} + +DECLCALLBACK(void) crFbTexRelease(CR_TEXDATA *pTex) +{ + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTex); + CRTextureObj *pTobj = pFbTex->pTobj; + + CrTdBltDataCleanupNe(pTex); + + if (pTobj) + { + CR_STATE_SHAREDOBJ_USAGE_CLEAR(pTobj, cr_server.MainContextInfo.pContext); + + crHashtableDelete(g_CrPresenter.pFbTexMap, pTobj->id, NULL); + + if (!CR_STATE_SHAREDOBJ_USAGE_IS_USED(pTobj)) + { + CRSharedState *pShared = crStateGlobalSharedAcquire(); + + CRASSERT(pShared); + /* on the host side, we need to delete an ogl texture object here as well, which crStateDeleteTextureCallback will do + * in addition to calling crStateDeleteTextureObject to delete a state object */ + crHashtableDelete(pShared->textureTable, pTobj->id, crStateDeleteTextureCallback); + + crStateGlobalSharedRelease(); + } + + crStateGlobalSharedRelease(); + } + + crFbTexFree(pFbTex); +} + +void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased) +{ + PCR_BLITTER pBlitter = crServerVBoxBlitterGet(); + + CrTdInit(pFbTex, pTex, pBlitter, pfnTextureReleased); +} + +static CR_FBTEX* crFbTexCreate(const VBOXVR_TEXTURE *pTex) +{ + CR_FBTEX *pFbTex = crFbTexAlloc(); + if (!pFbTex) + { + WARN(("crFbTexAlloc failed!")); + return NULL; + } + + CrFbTexDataInit(&pFbTex->Tex, pTex, crFbTexRelease); + pFbTex->pTobj = NULL; + + return pFbTex; +} + + +CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex) +{ + CR_FBTEX *pFbTex = crFbTexCreate(pTex); + if (!pFbTex) + { + WARN(("crFbTexCreate failed!")); + return NULL; + } + + return &pFbTex->Tex; +} + +static CR_FBTEX* crFbTexAcquire(GLuint idTexture) +{ + CR_FBTEX *pFbTex = (CR_FBTEX *)crHashtableSearch(g_CrPresenter.pFbTexMap, idTexture); + if (pFbTex) + { + CrTdAddRef(&pFbTex->Tex); + return pFbTex; + } + + CRSharedState *pShared = crStateGlobalSharedAcquire(); + if (!pShared) + { + WARN(("pShared is null!")); + return NULL; + } + + CRTextureObj *pTobj = (CRTextureObj*)crHashtableSearch(pShared->textureTable, idTexture); + if (!pTobj) + { + LOG(("pTobj is null!")); + crStateGlobalSharedRelease(); + return NULL; + } + + Assert(pTobj->id == idTexture); + + GLuint hwid = crStateGetTextureObjHWID(pTobj); + if (!hwid) + { + WARN(("hwId is null!")); + crStateGlobalSharedRelease(); + return NULL; + } + + VBOXVR_TEXTURE Tex; + Tex.width = pTobj->level[0]->width; + Tex.height = pTobj->level[0]->height; + Tex.hwid = hwid; + Tex.target = pTobj->target; + + pFbTex = crFbTexCreate(&Tex); + if (!pFbTex) + { + WARN(("crFbTexCreate failed!")); + crStateGlobalSharedRelease(); + return NULL; + } + + CR_STATE_SHAREDOBJ_USAGE_SET(pTobj, cr_server.MainContextInfo.pContext); + + pFbTex->pTobj = pTobj; + + crHashtableAdd(g_CrPresenter.pFbTexMap, idTexture, pFbTex); + + return pFbTex; +} + +static void crFbEntryMarkDestroyed(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + if (pEntry->Flags.fCreateNotified) + { + pEntry->Flags.fCreateNotified = 0; + if (pFb->pDisplay) + pFb->pDisplay->EntryDestroyed(pFb, pEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } +} + +static void crFbEntryDestroy(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + crFbEntryMarkDestroyed(pFb, pEntry); + CrVrScrCompositorEntryCleanup(&pEntry->Entry); + CrHTableDestroy(&pEntry->HTable); + Assert(pFb->cEntries); + RTListNodeRemove(&pEntry->Node); + --pFb->cEntries; + crFbEntryFree(pEntry); +} + +DECLINLINE(uint32_t) crFbEntryAddRef(CR_FRAMEBUFFER_ENTRY* pEntry) +{ + return ++pEntry->cRefs; +} + +DECLINLINE(uint32_t) crFbEntryRelease(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + uint32_t cRefs = --pEntry->cRefs; + if (!cRefs) + crFbEntryDestroy(pFb, pEntry); + return cRefs; +} + +static DECLCALLBACK(void) crFbEntryReleased(const struct VBOXVR_SCR_COMPOSITOR *pCompositor, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacingEntry) +{ + CR_FRAMEBUFFER *pFb = PCR_FRAMEBUFFER_FROM_COMPOSITOR(pCompositor); + CR_FRAMEBUFFER_ENTRY *pFbEntry = PCR_FBENTRY_FROM_ENTRY(pEntry); + CR_FRAMEBUFFER_ENTRY *pFbReplacingEntry = pReplacingEntry ? PCR_FBENTRY_FROM_ENTRY(pReplacingEntry) : NULL; + if (pFbReplacingEntry) + { + /*replace operation implies the replaced entry gets auto-destroyed, + * while all its data gets moved to the *clean* replacing entry + * 1. ensure the replacing entry is cleaned up */ + crFbEntryMarkDestroyed(pFb, pFbReplacingEntry); + + CrHTableMoveTo(&pFbEntry->HTable, &pFbReplacingEntry->HTable); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry); + CR_TEXDATA *pReplacingTex = CrVrScrCompositorEntryTexGet(&pFbReplacingEntry->Entry); + + CrTdBltScaleCacheMoveTo(pTex, pReplacingTex); + + if (pFb->pDisplay) + pFb->pDisplay->EntryReplaced(pFb, pFbReplacingEntry, pFbEntry); + + CrTdBltDataInvalidateNe(pTex); + + /* 2. mark the replaced entry is destroyed */ + Assert(pFbEntry->Flags.fCreateNotified); + Assert(pFbEntry->Flags.fInList); + pFbEntry->Flags.fCreateNotified = 0; + pFbEntry->Flags.fInList = 0; + pFbReplacingEntry->Flags.fCreateNotified = 1; + pFbReplacingEntry->Flags.fInList = 1; + } + else + { + if (pFbEntry->Flags.fInList) + { + pFbEntry->Flags.fInList = 0; + if (pFb->pDisplay) + pFb->pDisplay->EntryRemoved(pFb, pFbEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + + crFbEntryRelease(pFb, pFbEntry); +} + +static CR_FRAMEBUFFER_ENTRY* crFbEntryCreate(CR_FRAMEBUFFER *pFb, CR_TEXDATA* pTex, const RTRECT *pRect, uint32_t fFlags) +{ + CR_FRAMEBUFFER_ENTRY *pEntry = crFbEntryAlloc(); + if (!pEntry) + { + WARN(("crFbEntryAlloc failed!")); + return NULL; + } + + CrVrScrCompositorEntryInit(&pEntry->Entry, pRect, pTex, crFbEntryReleased); + CrVrScrCompositorEntryFlagsSet(&pEntry->Entry, fFlags); + pEntry->cRefs = 1; + pEntry->Flags.Value = 0; + CrHTableCreate(&pEntry->HTable, 0); + + RTListAppend(&pFb->EntriesList, &pEntry->Node); + ++pFb->cEntries; + + return pEntry; +} + +int CrFbEntryCreateForTexData(CR_FRAMEBUFFER *pFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry) +{ + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = pTex->Tex.width; + Rect.yBottom = pTex->Tex.height; + CR_FRAMEBUFFER_ENTRY* pEntry = crFbEntryCreate(pFb, pTex, &Rect, fFlags); + if (!pEntry) + { + WARN(("crFbEntryCreate failed")); + return VERR_NO_MEMORY; + } + + *phEntry = pEntry; + return VINF_SUCCESS; +} + +int CrFbEntryTexDataUpdate(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + if (pTex) + CrVrScrCompositorEntryTexSet(&pEntry->Entry, pTex); + + if (CrVrScrCompositorEntryIsUsed(&pEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, pEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + + return VINF_SUCCESS; +} + + +int CrFbEntryCreateForTexId(CR_FRAMEBUFFER *pFb, GLuint idTexture, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry) +{ + CR_FBTEX* pFbTex = crFbTexAcquire(idTexture); + if (!pFbTex) + { + LOG(("crFbTexAcquire failed")); + return VERR_INVALID_PARAMETER; + } + + CR_TEXDATA* pTex = &pFbTex->Tex; + int rc = CrFbEntryCreateForTexData(pFb, pTex, fFlags, phEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexData failed rc %d", rc)); + } + + /*always release the tex, the CrFbEntryCreateForTexData will do incref as necessary */ + CrTdRelease(pTex); + return rc; +} + +void CrFbEntryAddRef(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + ++hEntry->cRefs; +} + +void CrFbEntryRelease(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + crFbEntryRelease(pFb, hEntry); +} + +int CrFbRegionsClear(HCR_FRAMEBUFFER hFb) +{ + if (!hFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + bool fChanged = false; + CrVrScrCompositorRegionsClear(&hFb->Compositor, &fChanged); + if (fChanged) + { + if (hFb->pDisplay) + hFb->pDisplay->RegionsChanged(hFb); + } + + return VINF_SUCCESS; +} + +int CrFbEntryRegionsAdd(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + uint32_t fChangeFlags = 0; + VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL; + VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry; + bool fEntryWasInList; + + if (hEntry) + { + crFbEntryAddRef(hEntry); + pNewEntry = &hEntry->Entry; + fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry); + + Assert(!hEntry->Flags.fInList == !fEntryWasInList); + } + else + { + pNewEntry = NULL; + fEntryWasInList = false; + } + + int rc = CrVrScrCompositorEntryRegionsAdd(&pFb->Compositor, hEntry ? &hEntry->Entry : NULL, pPos, cRegions, paRegions, fPosRelated, &pReplacedScrEntry, &fChangeFlags); + if (RT_SUCCESS(rc)) + { + if (fChangeFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED) + { + if (!fEntryWasInList && pNewEntry) + { + Assert(CrVrScrCompositorEntryIsUsed(pNewEntry)); + if (!hEntry->Flags.fCreateNotified) + { + hEntry->Flags.fCreateNotified = 1; + if (pFb->pDisplay) + pFb->pDisplay->EntryCreated(pFb, hEntry); + } + +#ifdef DEBUG_misha + /* in theory hEntry->Flags.fInList can be set if entry is replaced, + * but then modified to fit the compositor rects, + * and so we get the regions changed notification as a result + * this should not generally happen though, so put an assertion to debug that situation */ + Assert(!hEntry->Flags.fInList); +#endif + if (!hEntry->Flags.fInList) + { + hEntry->Flags.fInList = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryAdded(pFb, hEntry); + } + } + if (pFb->pDisplay) + pFb->pDisplay->RegionsChanged(pFb); + + Assert(!pReplacedScrEntry); + } + else if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED) + { + Assert(pReplacedScrEntry); + /* we have already processed that in a "release" callback */ + Assert(hEntry); + } + else + { + Assert(!fChangeFlags); + Assert(!pReplacedScrEntry); + } + + if (hEntry) + { + if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, hEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + } + else + WARN(("CrVrScrCompositorEntryRegionsAdd failed, rc %d", rc)); + + return rc; +} + +int CrFbEntryRegionsSet(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + bool fChanged = 0; + VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL; + VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry; + bool fEntryWasInList; + + if (hEntry) + { + crFbEntryAddRef(hEntry); + pNewEntry = &hEntry->Entry; + fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry); + Assert(!hEntry->Flags.fInList == !fEntryWasInList); + } + else + { + pNewEntry = NULL; + fEntryWasInList = false; + } + + int rc = CrVrScrCompositorEntryRegionsSet(&pFb->Compositor, pNewEntry, pPos, cRegions, paRegions, fPosRelated, &fChanged); + if (RT_SUCCESS(rc)) + { + if (fChanged) + { + if (!fEntryWasInList && pNewEntry) + { + if (CrVrScrCompositorEntryIsUsed(pNewEntry)) + { + if (!hEntry->Flags.fCreateNotified) + { + hEntry->Flags.fCreateNotified = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryCreated(pFb, hEntry); + } + + Assert(!hEntry->Flags.fInList); + hEntry->Flags.fInList = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryAdded(pFb, hEntry); + } + } + + if (pFb->pDisplay) + pFb->pDisplay->RegionsChanged(pFb); + } + + if (hEntry) + { + if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, hEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + } + else + WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc)); + + return rc; +} + +const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry) +{ + return &hEntry->Entry; +} + +HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry) +{ + return RT_FROM_MEMBER(pCEntry, CR_FRAMEBUFFER_ENTRY, Entry); +} + +void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext) +{ + HCR_FRAMEBUFFER_ENTRY hEntry, hNext; + RTListForEachSafe(&hFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node) + { + if (hEntry->Flags.fCreateNotified) + { + if (!pfnVisitorCb(hFb, hEntry, pvContext)) + return; + } + } +} + + +CRHTABLE_HANDLE CrFbDDataAllocSlot(CR_FRAMEBUFFER *pFb) +{ + return CrHTablePut(&pFb->SlotTable, (void*)1); +} + +void CrFbDDataReleaseSlot(CR_FRAMEBUFFER *pFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext) +{ + HCR_FRAMEBUFFER_ENTRY hEntry, hNext; + RTListForEachSafe(&pFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node) + { + if (CrFbDDataEntryGet(hEntry, hSlot)) + { + if (pfnReleaseCb) + pfnReleaseCb(pFb, hEntry, pvContext); + + CrFbDDataEntryClear(hEntry, hSlot); + } + } + + CrHTableRemove(&pFb->SlotTable, hSlot); +} + +int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData) +{ + return CrHTablePutToSlot(&hEntry->HTable, hSlot, pvData); +} + +void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot) +{ + return CrHTableRemove(&hEntry->HTable, hSlot); +} + +void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot) +{ + return CrHTableGet(&hEntry->HTable, hSlot); +} + +typedef union CR_FBDISPBASE_FLAGS +{ + struct { + uint32_t fRegionsShanged : 1; + uint32_t Reserved : 31; + }; + uint32_t u32Value; +} CR_FBDISPBASE_FLAGS; + +class CrFbDisplayBase : public ICrFbDisplay +{ +public: + CrFbDisplayBase() : + mpContainer(NULL), + mpFb(NULL), + mcUpdates(0), + mhSlot(CRHTABLE_HANDLE_INVALID) + { + mFlags.u32Value = 0; + } + + virtual bool isComposite() + { + return false; + } + + class CrFbDisplayComposite* getContainer() + { + return mpContainer; + } + + bool isInList() + { + return !!mpContainer; + } + + bool isUpdating() + { + return !!mcUpdates; + } + + int setRegionsChanged() + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; + } + + int setFramebuffer(struct CR_FRAMEBUFFER *pFb) + { + if (mcUpdates) + { + WARN(("trying to set framebuffer while update is in progress")); + return VERR_INVALID_STATE; + } + + if (mpFb == pFb) + return VINF_SUCCESS; + + int rc = setFramebufferBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpFb) + { + rc = fbCleanup(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + setFramebufferEnd(pFb); + return rc; + } + } + + mpFb = pFb; + + if (mpFb) + { + rc = fbSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + setFramebufferEnd(pFb); + return rc; + } + } + + setFramebufferEnd(pFb); + return VINF_SUCCESS; + } + + struct CR_FRAMEBUFFER* getFramebuffer() + { + return mpFb; + } + + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) + { + ++mcUpdates; + Assert(!mFlags.fRegionsShanged || mcUpdates > 1); + return VINF_SUCCESS; + } + + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) + { + --mcUpdates; + Assert(mcUpdates < UINT32_MAX/2); + if (!mcUpdates) + onUpdateEnd(); + } + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; + } + + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; + } + + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; + } + + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; + } + + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + return VINF_SUCCESS; + } + + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; + } + + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; + } + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) + { + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; + } + + virtual ~CrFbDisplayBase(); + + /*@todo: move to protected and switch from RTLISTNODE*/ + RTLISTNODE mNode; + class CrFbDisplayComposite* mpContainer; +protected: + virtual void onUpdateEnd() + { + if (mFlags.fRegionsShanged) + { + mFlags.fRegionsShanged = 0; + if (getFramebuffer()) /*<-dont't do anything on cleanup*/ + ueRegions(); + } + } + + virtual void ueRegions() + { + } + + static DECLCALLBACK(bool) entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) + { + int rc = ((ICrFbDisplay*)(pvContext))->EntryCreated(hFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + } + return true; + } + + static DECLCALLBACK(bool) entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) + { + int rc = ((ICrFbDisplay*)(pvContext))->EntryDestroyed(hFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + } + return true; + } + + int fbSynchAddAllEntries() + { + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter); + + int rc = VINF_SUCCESS; + + CrFbVisitCreatedEntries(mpFb, entriesCreateCb, this); + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + + rc = EntryAdded(mpFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + EntryDestroyed(mpFb, hEntry); + break; + } + } + + return rc; + } + + int fbCleanupRemoveAllEntries() + { + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter); + + int rc = VINF_SUCCESS; + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + rc = EntryRemoved(mpFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + break; + } + + CrFbVisitCreatedEntries(mpFb, entriesDestroyCb, this); + } + + return rc; + } + + virtual int setFramebufferBegin(struct CR_FRAMEBUFFER *pFb) + { + return UpdateBegin(pFb); + } + virtual void setFramebufferEnd(struct CR_FRAMEBUFFER *pFb) + { + UpdateEnd(pFb); + } + + static DECLCALLBACK(void) slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) + { + } + + virtual void slotRelease() + { + Assert(mhSlot); + CrFbDDataReleaseSlot(mpFb, mhSlot, slotEntryReleaseCB, this); + } + + virtual int fbCleanup() + { + if (mhSlot) + { + slotRelease(); + mhSlot = 0; + } + mpFb = NULL; + return VINF_SUCCESS; + } + + virtual int fbSync() + { + return VINF_SUCCESS; + } + + CRHTABLE_HANDLE slotGet() + { + if (!mhSlot) + { + if (mpFb) + mhSlot = CrFbDDataAllocSlot(mpFb); + } + + return mhSlot; + } + +private: + struct CR_FRAMEBUFFER *mpFb; + uint32_t mcUpdates; + CRHTABLE_HANDLE mhSlot; + CR_FBDISPBASE_FLAGS mFlags; +}; + +class CrFbDisplayComposite : public CrFbDisplayBase +{ +public: + CrFbDisplayComposite() : + mcDisplays(0) + { + RTListInit(&mDisplays); + } + + virtual bool isComposite() + { + return true; + } + + uint32_t getDisplayCount() + { + return mcDisplays; + } + + bool add(CrFbDisplayBase *pDisplay) + { + if (pDisplay->isInList()) + { + WARN(("entry in list already")); + return false; + } + + RTListAppend(&mDisplays, &pDisplay->mNode); + pDisplay->mpContainer = this; + pDisplay->setFramebuffer(getFramebuffer()); + ++mcDisplays; + return true; + } + + bool remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay = true) + { + if (pDisplay->getContainer() != this) + { + WARN(("invalid entry container")); + return false; + } + + RTListNodeRemove(&pDisplay->mNode); + pDisplay->mpContainer = NULL; + if (fCleanupDisplay) + pDisplay->setFramebuffer(NULL); + --mcDisplays; + return true; + } + + CrFbDisplayBase* first() + { + return RTListGetFirstCpp(&mDisplays, CrFbDisplayBase, mNode); + } + + CrFbDisplayBase* next(CrFbDisplayBase* pDisplay) + { + if (pDisplay->getContainer() != this) + { + WARN(("invalid entry container")); + return NULL; + } + + return RTListGetNextCpp(&mDisplays, pDisplay, CrFbDisplayBase, mNode); + } + + virtual int setFramebuffer(struct CR_FRAMEBUFFER *pFb) + { + CrFbDisplayBase::setFramebuffer(pFb); + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + pIter->setFramebuffer(pFb); + } + + return VINF_SUCCESS; + } + + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::UpdateBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + rc = pIter->UpdateBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) + { + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + pIter->UpdateEnd(pFb); + } + + CrFbDisplayBase::UpdateEnd(pFb); + } + + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; + } + + virtual ~CrFbDisplayComposite() + { + cleanup(); + } + + void cleanup(bool fCleanupDisplays = true) + { + CrFbDisplayBase *pIter, *pIterNext; + RTListForEachSafeCpp(&mDisplays, pIter, pIterNext, CrFbDisplayBase, mNode) + { + remove(pIter, fCleanupDisplays); + } + } +private: + RTLISTNODE mDisplays; + uint32_t mcDisplays; +}; + +typedef union CR_FBWIN_FLAGS +{ + struct { + uint32_t fVisible : 1; + uint32_t fDataPresented : 1; + uint32_t fForcePresentOnReenable : 1; + uint32_t fCompositoEntriesModified : 1; + uint32_t Reserved : 28; + }; + uint32_t Value; +} CR_FBWIN_FLAGS; + +class CrFbWindow +{ +public: + CrFbWindow(uint64_t parentId) : + mSpuWindow(0), + mpCompositor(NULL), + mcUpdates(0), + mxPos(0), + myPos(0), + mWidth(0), + mHeight(0), + mParentId(parentId) + { + mFlags.Value = 0; + } + + bool IsCreated() + { + return !!mSpuWindow; + } + + void Destroy() + { + CRASSERT(!mcUpdates); + + if (!mSpuWindow) + return; + + cr_server.head_spu->dispatch_table.WindowDestroy(mSpuWindow); + + mSpuWindow = 0; + mFlags.fDataPresented = 0; + } + + int Reparent(uint64_t parentId) + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + uint64_t oldParentId = mParentId; + + mParentId = parentId; + + if (mSpuWindow) + { + if (oldParentId && !parentId && mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, false); + + renderspuSetWindowId(mParentId); + renderspuReparentWindow(mSpuWindow); + renderspuSetWindowId(cr_server.screen[0].winID); + + if (parentId) + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos); + + if (!oldParentId && parentId && mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, true); + } + + return VINF_SUCCESS; + } + + int SetVisible(bool fVisible) + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + LOG(("CrWIN: Vidible [%d]", fVisible)); + + if (!fVisible != !mFlags.fVisible) + { + mFlags.fVisible = fVisible; + if (mSpuWindow && mParentId) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, fVisible); + } + + return VINF_SUCCESS; + } + + int SetSize(uint32_t width, uint32_t height) + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + LOG(("CrWIN: Size [%d ; %d]", width, height)); + + if (mWidth != width || mHeight != height) + { + mFlags.fCompositoEntriesModified = 1; + mWidth = width; + mHeight = height; + if (mSpuWindow) + cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, width, height); + } + + return VINF_SUCCESS; + } + + int SetPosition(int32_t x, int32_t y) + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + LOG(("CrWIN: Pos [%d ; %d]", x, y)); +// always do WindowPosition to ensure window is adjusted properly +// if (x != mxPos || y != myPos) + { + mxPos = x; + myPos = y; + if (mSpuWindow) + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, x, y); + } + + return VINF_SUCCESS; + } + + int SetVisibleRegionsChanged() + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mFlags.fCompositoEntriesModified = 1; + return VINF_SUCCESS; + } + + int SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor) + { + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mpCompositor = pCompositor; + mFlags.fCompositoEntriesModified = 1; + return VINF_SUCCESS; + } + + int UpdateBegin() + { + ++mcUpdates; + if (mcUpdates > 1) + return VINF_SUCCESS; + + Assert(!mFlags.fForcePresentOnReenable); +// Assert(!mFlags.fCompositoEntriesModified); + + if (mFlags.fDataPresented) + { + Assert(mSpuWindow); + cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, NULL, NULL); + mFlags.fForcePresentOnReenable = isPresentNeeded(); + } + + return VINF_SUCCESS; + } + + void UpdateEnd() + { + --mcUpdates; + Assert(mcUpdates < UINT32_MAX/2); + if (mcUpdates) + return; + + checkRegions(); + + if (mSpuWindow) + { + bool fPresentNeeded = isPresentNeeded(); + if (fPresentNeeded || mFlags.fForcePresentOnReenable) + { + mFlags.fForcePresentOnReenable = false; + cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, mpCompositor, NULL); + } + + /* even if the above branch is entered due to mFlags.fForcePresentOnReenable, + * the backend should clean up the compositor as soon as presentation is performed */ + mFlags.fDataPresented = fPresentNeeded; + } + else + { + Assert(!mFlags.fDataPresented); + Assert(!mFlags.fForcePresentOnReenable); + } + } + + uint64_t GetParentId() + { + return mParentId; + } + + int Create() + { + if (mSpuWindow) + { + //WARN(("window already created")); + return VINF_ALREADY_INITIALIZED; + } + + CRASSERT(cr_server.fVisualBitsDefault); + renderspuSetWindowId(mParentId); + mSpuWindow = cr_server.head_spu->dispatch_table.WindowCreate("", cr_server.fVisualBitsDefault); + renderspuSetWindowId(cr_server.screen[0].winID); + if (mSpuWindow < 0) { + WARN(("WindowCreate failed")); + return VERR_GENERAL_FAILURE; + } + + cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, mWidth, mHeight); + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos); + + checkRegions(); + + if (mParentId && mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, true); + + return VINF_SUCCESS; + } + + ~CrFbWindow() + { + Destroy(); + } +protected: + void checkRegions() + { + if (!mSpuWindow) + return; + + if (!mFlags.fCompositoEntriesModified) + return; + + uint32_t cRects; + const RTRECT *pRects; + if (mpCompositor) + { + int rc = CrVrScrCompositorRegionsGet(mpCompositor, &cRects, NULL, &pRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc)); + cRects = 0; + pRects = NULL; + } + } + else + { + cRects = 0; + pRects = NULL; + } + + cr_server.head_spu->dispatch_table.WindowVisibleRegion(mSpuWindow, cRects, (const GLint*)pRects); + + mFlags.fCompositoEntriesModified = 0; + } + + bool isPresentNeeded() + { + return mFlags.fVisible && mWidth && mHeight && mpCompositor && !CrVrScrCompositorIsEmpty(mpCompositor); + } + + bool checkInitedUpdating() + { + if (!mcUpdates) + { + WARN(("not updating")); + return false; + } + + return true; + } +private: + GLint mSpuWindow; + const struct VBOXVR_SCR_COMPOSITOR * mpCompositor; + uint32_t mcUpdates; + int32_t mxPos; + int32_t myPos; + uint32_t mWidth; + uint32_t mHeight; + CR_FBWIN_FLAGS mFlags; + uint64_t mParentId; +}; + +typedef union CR_FBDISPWINDOW_FLAGS +{ + struct { + uint32_t fNeVisible : 1; + uint32_t fNeForce : 1; + uint32_t Reserved : 30; + }; + uint32_t u32Value; +} CR_FBDISPWINDOW_FLAGS; +class CrFbDisplayWindow : public CrFbDisplayBase +{ +public: + CrFbDisplayWindow(CrFbWindow *pWindow, const RTRECT *pViewportRect) : + mpWindow(pWindow), + mViewportRect(*pViewportRect), + mu32Screen(~0) + { + mFlags.u32Value = 0; + CRASSERT(pWindow); + } + + virtual ~CrFbDisplayWindow() + { + if (mpWindow) + delete mpWindow; + } + + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) + { + int rc = mpWindow->UpdateBegin(); + if (RT_SUCCESS(rc)) + { + rc = CrFbDisplayBase::UpdateBegin(pFb); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + WARN(("err")); + } + else + WARN(("err")); + + return rc; + } + + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) + { + CrFbDisplayBase::UpdateEnd(pFb); + + mpWindow->UpdateEnd(); + } + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; + } + + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; + } + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return screenChanged(); + } + + const RTRECT* getViewportRect() + { + return &mViewportRect; + } + + virtual int setViewportRect(const RTRECT *pViewportRect) + { + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + +// always call SetPosition to ensure window is adjustep properly +// if (pViewportRect->xLeft != mViewportRect.xLeft || pViewportRect->yTop != mViewportRect.yTop) + { + const RTRECT* pRect = getRect(); + int rc = mpWindow->SetPosition(pRect->xLeft - pViewportRect->xLeft, pRect->yTop - pViewportRect->yTop); + if (!RT_SUCCESS(rc)) + { + WARN(("SetPosition failed")); + return rc; + } + } + + mViewportRect = *pViewportRect; + + return VINF_SUCCESS; + } + + virtual CrFbWindow * windowDetach() + { + if (isUpdating()) + { + WARN(("updating!")); + return NULL; + } + + CrFbWindow * pWindow = mpWindow; + if (mpWindow) + { + windowCleanup(); + mpWindow = NULL; + } + return pWindow; + } + + virtual CrFbWindow * windowAttach(CrFbWindow * pNewWindow) + { + if (isUpdating()) + { + WARN(("updating!")); + return NULL; + } + + CrFbWindow * pOld = mpWindow; + if (mpWindow) + windowDetach(); + + mpWindow = pNewWindow; + if (pNewWindow) + windowSync(); + + return mpWindow; + } + + virtual int reparent(uint64_t parentId) + { + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + + int rc = mpWindow->Reparent(parentId); + if (!RT_SUCCESS(rc)) + WARN(("window reparent failed")); + + mFlags.fNeForce = 1; + + return rc; + } + + virtual bool isVisible() + { + HCR_FRAMEBUFFER hFb = getFramebuffer(); + if (!hFb) + return false; + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb); + return !CrVrScrCompositorIsEmpty(pCompositor); + } + + int winVisibilityChanged() + { + int rc = mpWindow->UpdateBegin(); + if (RT_SUCCESS(rc)) + { + rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden); + if (!RT_SUCCESS(rc)) + WARN(("SetVisible failed, rc %d", rc)); + + mpWindow->UpdateEnd(); + } + else + WARN(("UpdateBegin failed, rc %d", rc)); + + return rc; + } + +protected: + virtual void onUpdateEnd() + { + CrFbDisplayBase::onUpdateEnd(); + bool fVisible = isVisible(); + if (mFlags.fNeVisible != fVisible || mFlags.fNeForce) + { + crVBoxServerNotifyEvent(mu32Screen, VBOX3D_NOTIFY_EVENT_TYPE_VISIBLE_3DDATA, fVisible ? (void*)1 : NULL); + mFlags.fNeVisible = fVisible; + mFlags.fNeForce = 0; + } + } + + virtual void ueRegions() + { + mpWindow->SetVisibleRegionsChanged(); + } + + virtual int screenChanged() + { + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + + if (CrFbIsEnabled(getFramebuffer())) + { + const RTRECT* pRect = getRect(); + int rc = mpWindow->SetPosition(pRect->xLeft - mViewportRect.xLeft, pRect->yTop - mViewportRect.yTop); + if (!RT_SUCCESS(rc)) + { + WARN(("SetComposition failed rc %d", rc)); + return rc; + } + + setRegionsChanged(); + + return mpWindow->SetSize((uint32_t)(pRect->xRight - pRect->xLeft), (uint32_t)(pRect->yBottom - pRect->yTop)); + } + + return mpWindow->SetVisible(false); + } + + virtual int windowSetCompositor(bool fSet) + { + if (fSet) + { + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer()); + return mpWindow->SetCompositor(pCompositor); + } + return mpWindow->SetCompositor(NULL); + } + + virtual int windowCleanup() + { + int rc = mpWindow->UpdateBegin(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = mpWindow->SetVisible(false); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = windowSetCompositor(false); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + mpWindow->UpdateEnd(); + + return VINF_SUCCESS; + } + + virtual int fbCleanup() + { + int rc = windowCleanup(); + if (!RT_SUCCESS(rc)) + { + WARN(("windowCleanup failed")); + return rc; + } + return CrFbDisplayBase::fbCleanup(); + } + + virtual int windowSync() + { + const RTRECT* pRect = getRect(); + + int rc = mpWindow->UpdateBegin(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = windowSetCompositor(true); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = mpWindow->SetPosition(pRect->xLeft - mViewportRect.xLeft, pRect->yTop - mViewportRect.yTop); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = mpWindow->SetSize((uint32_t)(pRect->xRight - pRect->xLeft), (uint32_t)(pRect->yBottom - pRect->yTop)); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + mpWindow->UpdateEnd(); + + return rc; + } + + virtual int fbSync() + { + int rc = CrFbDisplayBase::fbSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + mu32Screen = CrFbGetScreenInfo(getFramebuffer())->u32ViewIndex; + + return windowSync(); + } + + virtual const struct RTRECT* getRect() + { + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer()); + return CrVrScrCompositorRectGet(pCompositor); + } + + CrFbWindow* getWindow() {return mpWindow;} +private: + CrFbWindow *mpWindow; + RTRECT mViewportRect; + CR_FBDISPWINDOW_FLAGS mFlags; + uint32_t mu32Screen; +}; + +class CrFbDisplayWindowRootVr : public CrFbDisplayWindow +{ +public: + CrFbDisplayWindowRootVr(CrFbWindow *pWindow, const RTRECT *pViewportRect) : + CrFbDisplayWindow(pWindow, pViewportRect) + { + CrVrScrCompositorInit(&mCompositor, NULL); + } + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayWindow::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + Assert(!CrFbDDataEntryGet(hEntry, slotGet())); + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = entryAlloc(); + CrVrScrCompositorEntryInit(pMyEntry, CrVrScrCompositorEntryRectGet(pSrcEntry), CrVrScrCompositorEntryTexGet(pSrcEntry), NULL); + CrVrScrCompositorEntryFlagsSet(pMyEntry, CrVrScrCompositorEntryFlagsGet(pSrcEntry)); + rc = CrFbDDataEntryPut(hEntry, slotGet(), pMyEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbDDataEntryPut failed rc %d", rc)); + entryFree(pMyEntry); + return rc; + } + + return VINF_SUCCESS; + } + + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayWindow::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + Assert(pMyEntry); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry)); + + return VINF_SUCCESS; + } + + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + int rc = CrFbDisplayWindow::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcNewEntry = CrFbEntryGetCompositorEntry(hNewEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hNewEntry, slotGet()); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcNewEntry)); + + return VINF_SUCCESS; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayWindow::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry)); + + return VINF_SUCCESS; + } + + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayWindow::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + rc = CrVrScrCompositorEntryRegionsSet(&mCompositor, pMyEntry, NULL, 0, NULL, false, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; + } + + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayWindow::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + CrVrScrCompositorEntryCleanup(pMyEntry); + entryFree(pMyEntry); + + return VINF_SUCCESS; + } + + virtual int setViewportRect(const RTRECT *pViewportRect) + { + int rc = CrFbDisplayWindow::setViewportRect(pViewportRect); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = setRegionsChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; + } + +protected: + virtual int windowSetCompositor(bool fSet) + { + if (fSet) + return getWindow()->SetCompositor(&mCompositor); + return getWindow()->SetCompositor(NULL); + } + + virtual void ueRegions() + { + synchCompositorRegions(); + } + + int compositorMarkUpdated() + { + CrVrScrCompositorClear(&mCompositor); + + int rc = CrVrScrCompositorRectSet(&mCompositor, CrVrScrCompositorRectGet(CrFbGetCompositor(getFramebuffer())), NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = setRegionsChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("screenChanged failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; + } + + virtual int screenChanged() + { + int rc = compositorMarkUpdated(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = CrFbDisplayWindow::screenChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("screenChanged failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; + } + + virtual const struct RTRECT* getRect() + { + return CrVrScrCompositorRectGet(&mCompositor); + } + + virtual int fbCleanup() + { + int rc = clearCompositor(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayWindow::fbCleanup(); + } + + virtual int fbSync() + { + int rc = synchCompositor(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayWindow::fbSync(); + } + + VBOXVR_SCR_COMPOSITOR_ENTRY* entryAlloc() + { +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemCacheAlloc(g_CrPresenter.CEntryLookasideList); +#else + return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemAlloc(sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY)); +#endif + } + + void entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry) + { + Assert(!CrVrScrCompositorEntryIsUsed(pEntry)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.CEntryLookasideList, pEntry); +#else + RTMemFree(pEntry); +#endif + } + + int synchCompositorRegions() + { + int rc; + + rootVrTranslateForPos(); + + /* ensure the rootvr compositor does not hold any data, + * i.e. cleanup all rootvr entries data */ + CrVrScrCompositorClear(&mCompositor); + + rc = CrVrScrCompositorIntersectedList(CrFbGetCompositor(getFramebuffer()), &cr_server.RootVr, &mCompositor, rootVrGetCEntry, this, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorIntersectedList failed, rc %d", rc)); + return rc; + } + + return getWindow()->SetVisibleRegionsChanged(); + } + + virtual int synchCompositor() + { + int rc = compositorMarkUpdated(); + if (!RT_SUCCESS(rc)) + { + WARN(("compositorMarkUpdated failed, rc %d", rc)); + return rc; + } + + rc = fbSynchAddAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("fbSynchAddAllEntries failed, rc %d", rc)); + return rc; + } + + return rc; + } + + virtual int clearCompositor() + { + return fbCleanupRemoveAllEntries(); + } + + void rootVrTranslateForPos() + { + const RTRECT *pRect = getViewportRect(); + const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(getFramebuffer()); + int32_t x = pScreen->i32OriginX; + int32_t y = pScreen->i32OriginY; + int32_t dx = cr_server.RootVrCurPoint.x - x; + int32_t dy = cr_server.RootVrCurPoint.y - y; + + cr_server.RootVrCurPoint.x = x; + cr_server.RootVrCurPoint.y = y; + + VBoxVrListTranslate(&cr_server.RootVr, dx, dy); + } + + static DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext) + { + CrFbDisplayWindowRootVr *pThis = (CrFbDisplayWindowRootVr*)pvContext; + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, pThis->slotGet()); + Assert(!CrVrScrCompositorEntryIsUsed(pMyEntry)); + CrVrScrCompositorEntryRectSet(&pThis->mCompositor, pMyEntry, CrVrScrCompositorEntryRectGet(pEntry)); + return pMyEntry; + } +private: + VBOXVR_SCR_COMPOSITOR mCompositor; +}; + +class CrFbDisplayVrdp : public CrFbDisplayBase +{ +public: + CrFbDisplayVrdp() + { + memset(&mPos, 0, sizeof (mPos)); + } + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("EntryAdded failed rc %d", rc)); + return rc; + } + + Assert(!CrFbDDataEntryGet(hEntry, slotGet())); + rc = vrdpCreate(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("vrdpCreate failed rc %d", rc)); + return rc; + } + + return VINF_SUCCESS; + } + + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pReplacedEntry = CrFbEntryGetCompositorEntry(hReplacedEntry); + CR_TEXDATA *pReplacedTex = CrVrScrCompositorEntryTexGet(pReplacedEntry); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pNewEntry = CrFbEntryGetCompositorEntry(hNewEntry); + CR_TEXDATA *pNewTex = CrVrScrCompositorEntryTexGet(pNewEntry); + + CrTdBltDataInvalidateNe(pReplacedTex); + + rc = CrTdBltEnter(pNewTex); + if (RT_SUCCESS(rc)) + { + rc = vrdpFrame(hNewEntry); + CrTdBltLeave(pNewTex); + } + else + WARN(("CrTdBltEnter failed %d", rc)); + + return rc; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + + rc = CrTdBltEnter(pTex); + if (RT_SUCCESS(rc)) + { + rc = vrdpFrame(hEntry); + CrTdBltLeave(pTex); + } + else + WARN(("CrTdBltEnter failed %d", rc)); + + return rc; + } + + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + CrTdBltDataInvalidateNe(pTex); + + return vrdpRegions(pFb, hEntry); + } + + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + vrdpDestroy(hEntry); + return VINF_SUCCESS; + } + + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + int rc = CrFbDisplayBase::EntryPosChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + vrdpGeometry(hEntry); + + return VINF_SUCCESS; + } + + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return vrdpRegionsAll(pFb); + } + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) + { + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + syncPos(); + + rc = vrdpSyncEntryAll(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return vrdpRegionsAll(pFb); + } + +protected: + void syncPos() + { + const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(getFramebuffer()); + mPos.x = pScreenInfo->i32OriginX; + mPos.y = pScreenInfo->i32OriginY; + } + + virtual int fbCleanup() + { + int rc = fbCleanupRemoveAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayBase::fbCleanup(); + } + + virtual int fbSync() + { + syncPos(); + + int rc = fbSynchAddAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayBase::fbSync(); + } +protected: + void vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry) + { + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + cr_server.outputRedirect.CROREnd(pVrdp); + } + + void vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry) + { + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + + cr_server.outputRedirect.CRORGeometry(pVrdp, + mPos.x + CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + mPos.y + CrVrScrCompositorEntryRectGet(pEntry)->yTop, + CrVrScrCompositorEntryTexGet(pEntry)->Tex.width, + CrVrScrCompositorEntryTexGet(pEntry)->Tex.height); + } + + int vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + uint32_t cRects; + const RTRECT *pRects; + + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, NULL, &pRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc)); + return rc; + } + + cr_server.outputRedirect.CRORVisibleRegion(pVrdp, cRects, pRects); + return VINF_SUCCESS; + } + + int vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry) + { + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + const CR_BLITTER_IMG *pImg; + CrTdBltDataInvalidateNe(pTex); + int rc = CrTdBltDataAcquire(pTex, GL_BGRA, !!(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS), &pImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + return rc; + } + + cr_server.outputRedirect.CRORFrame(pVrdp, pImg->pvData, pImg->cbData); + CrTdBltDataRelease(pTex); + return VINF_SUCCESS; + } + + int vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb) + { + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(pCompositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + vrdpRegions(pFb, hEntry); + } + + return VINF_SUCCESS; + } + + int vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + vrdpGeometry(hEntry); + + return vrdpRegions(pFb, hEntry);; + } + + int vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb) + { + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(pCompositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + int rc = vrdpSynchEntry(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("vrdpSynchEntry failed rc %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; + } + + int vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + void *pVrdp; + + /* Query supported formats. */ + uint32_t cbFormats = 4096; + char *pachFormats = (char *)crAlloc(cbFormats); + + if (!pachFormats) + { + WARN(("crAlloc failed")); + return VERR_NO_MEMORY; + } + + int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext, + 0 /* H3DOR_PROP_FORMATS */, // @todo from a header + pachFormats, cbFormats, &cbFormats); + if (RT_SUCCESS(rc)) + { + if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN")) + { + cr_server.outputRedirect.CRORBegin(cr_server.outputRedirect.pvContext, + &pVrdp, + "H3DOR_FMT_RGBA_TOPDOWN"); // @todo from a header + + if (pVrdp) + { + rc = CrFbDDataEntryPut(hEntry, slotGet(), pVrdp); + if (RT_SUCCESS(rc)) + { + vrdpGeometry(hEntry); + vrdpRegions(hFb, hEntry); + //vrdpFrame(hEntry); + return VINF_SUCCESS; + } + else + WARN(("CrFbDDataEntryPut failed rc %d", rc)); + + cr_server.outputRedirect.CROREnd(pVrdp); + } + else + { + WARN(("CRORBegin failed")); + rc = VERR_GENERAL_FAILURE; + } + } + } + else + WARN(("CRORContextProperty failed rc %d", rc)); + + crFree(pachFormats); + + return rc; + } +private: + RTPOINT mPos; +}; + +CrFbDisplayBase::~CrFbDisplayBase() +{ + Assert(!mcUpdates); + + if (mpContainer) + mpContainer->remove(this); +} + + +#if 0 + + + + + +void crDbgDumpRect(uint32_t i, const RTRECT *pRect) +{ + crDebug("%d: (%d;%d) X (%d;%d)", i, pRect->xLeft, pRect->yTop, pRect->xRight, pRect->yBottom); +} + +void crDbgDumpRects(uint32_t cRects, const RTRECT *paRects) +{ + crDebug("Dumping rects (%d)", cRects); + for (uint32_t i = 0; i < cRects; ++i) + { + crDbgDumpRect(i, &paRects[i]); + } + crDebug("End Dumping rects (%d)", cRects); +} + +int crServerDisplaySaveState(PSSMHANDLE pSSM) +{ + int rc; + int cDisplays = 0, i; + for (i = 0; i < cr_server.screenCount; ++i) + { + if (ASMBitTest(cr_server.DisplaysInitMap, i) && !CrDpIsEmpty(&cr_server.aDispplays[i])) + ++cDisplays; + } + + rc = SSMR3PutS32(pSSM, cDisplays); + AssertRCReturn(rc, rc); + + if (!cDisplays) + return VINF_SUCCESS; + + rc = SSMR3PutS32(pSSM, cr_server.screenCount); + AssertRCReturn(rc, rc); + + for (i = 0; i < cr_server.screenCount; ++i) + { + rc = SSMR3PutS32(pSSM, cr_server.screen[i].x); + AssertRCReturn(rc, rc); + + rc = SSMR3PutS32(pSSM, cr_server.screen[i].y); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, cr_server.screen[i].w); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, cr_server.screen[i].h); + AssertRCReturn(rc, rc); + } + + for (i = 0; i < cr_server.screenCount; ++i) + { + if (ASMBitTest(cr_server.DisplaysInitMap, i) && !CrDpIsEmpty(&cr_server.aDispplays[i])) + { + rc = SSMR3PutS32(pSSM, i); + AssertRCReturn(rc, rc); + + rc = CrDpSaveState(&cr_server.aDispplays[i], pSSM); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + +int crServerDisplayLoadState(PSSMHANDLE pSSM, uint32_t u32Version) +{ + +} +#endif + +class CrFbDisplayEntryDataMonitor : public CrFbDisplayBase +{ +public: + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) + { + entryDataChanged(pFb, hReplacedEntry); + return VINF_SUCCESS; + } + + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + entryDataChanged(pFb, hEntry); + return VINF_SUCCESS; + } + + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + entryDataChanged(pFb, hEntry); + return VINF_SUCCESS; + } +protected: + virtual void entryDataChanged(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry) + { + + } +}; + +int CrPMgrInit() +{ + int rc = VINF_SUCCESS; + memset(&g_CrPresenter, 0, sizeof (g_CrPresenter)); + g_CrPresenter.pFbTexMap = crAllocHashtable(); + if (g_CrPresenter.pFbTexMap) + { +#ifndef VBOXVDBG_MEMCACHE_DISABLE + rc = RTMemCacheCreate(&g_CrPresenter.FbEntryLookasideList, sizeof (CR_FRAMEBUFFER_ENTRY), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { + rc = RTMemCacheCreate(&g_CrPresenter.FbTexLookasideList, sizeof (CR_FBTEX), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { + rc = RTMemCacheCreate(&g_CrPresenter.CEntryLookasideList, sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { +#endif + rc = crPMgrModeModifyGlobal(CR_PMGR_MODE_WINDOW, 0); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + WARN(("crPMgrModeModifyGlobal failed rc %d", rc)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); + + RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); + + RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); +#endif + } + else + { + WARN(("crAllocHashtable failed")); + rc = VERR_NO_MEMORY; + } + return rc; +} + +void CrPMgrTerm() +{ + crPMgrModeModifyGlobal(0, CR_PMGR_MODE_ALL); + + HCR_FRAMEBUFFER hFb; + + for (hFb = CrPMgrFbGetFirstInitialized(); + hFb; + hFb = CrPMgrFbGetNextInitialized(hFb)) + { + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CrFbDisplaySet(hFb, NULL); + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + + if (pInfo->pDpComposite) + delete pInfo->pDpComposite; + + Assert(!pInfo->pDpWin); + Assert(!pInfo->pDpWinRootVr); + Assert(!pInfo->pDpVrdp); + + CrFbTerm(hFb); + } + +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList); + RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList); + RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList); +#endif + crFreeHashtable(g_CrPresenter.pFbTexMap, NULL); + + if (g_CrPresenter.pvTmpBuf) + RTMemFree(g_CrPresenter.pvTmpBuf); + + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + memset(&g_CrPresenter, 0, sizeof (g_CrPresenter)); +} + +HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return NULL; + } + + if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idScreen)) + { + CrFbInit(&g_CrPresenter.aFramebuffers[idScreen], idScreen); + CrFBmSetAtomic(&g_CrPresenter.FramebufferInitMap, idScreen); + } + else + Assert(g_CrPresenter.aFramebuffers[idScreen].ScreenInfo.u32ViewIndex == idScreen); + + return &g_CrPresenter.aFramebuffers[idScreen]; +} + +HCR_FRAMEBUFFER CrPMgrFbGetInitialized(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return NULL; + } + + if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idScreen)) + { + return NULL; + } + else + Assert(g_CrPresenter.aFramebuffers[idScreen].ScreenInfo.u32ViewIndex == idScreen); + + return &g_CrPresenter.aFramebuffers[idScreen]; +} + +HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idScreen) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(idScreen); + + if(hFb && CrFbIsEnabled(hFb)) + return hFb; + + return NULL; +} + +static HCR_FRAMEBUFFER crPMgrFbGetNextEnabled(uint32_t i) +{ + for (;i < (uint32_t)cr_server.screenCount; ++i) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(i); + if (hFb) + return hFb; + } + + return NULL; +} + +static HCR_FRAMEBUFFER crPMgrFbGetNextInitialized(uint32_t i) +{ + for (;i < (uint32_t)cr_server.screenCount; ++i) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i); + if (hFb) + return hFb; + } + + return NULL; +} + +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled() +{ + HCR_FRAMEBUFFER hFb = crPMgrFbGetNextEnabled(0); +// if (!hFb) +// WARN(("no enabled framebuffer found")); + return hFb; +} + +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb) +{ + return crPMgrFbGetNextEnabled(hFb->ScreenInfo.u32ViewIndex+1); +} + +HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized() +{ + HCR_FRAMEBUFFER hFb = crPMgrFbGetNextInitialized(0); +// if (!hFb) +// WARN(("no initialized framebuffer found")); + return hFb; +} + +HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb) +{ + return crPMgrFbGetNextInitialized(hFb->ScreenInfo.u32ViewIndex+1); +} + +static uint32_t crPMgrModeAdjustVal(uint32_t u32Mode) +{ + u32Mode = CR_PMGR_MODE_ALL & u32Mode; + if (CR_PMGR_MODE_ROOTVR & u32Mode) + u32Mode &= ~CR_PMGR_MODE_WINDOW; + return u32Mode; +} + +int CrPMgrScreenChanged(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return VERR_INVALID_PARAMETER; + } + + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pInfo->pDpWin) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGet(idScreen); + if (CrFbIsUpdating(hFb)) + { + WARN(("trying to update viewport while framebuffer is being updated")); + return VERR_INVALID_STATE; + } + + int rc = pInfo->pDpWin->UpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + pInfo->pDpWin->reparent(cr_server.screen[idScreen].winID); + + pInfo->pDpWin->UpdateEnd(hFb); + } + else + WARN(("UpdateBegin failed %d", rc)); + } + + return VINF_SUCCESS; +} + +int CrPMgrViewportUpdate(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return VERR_INVALID_PARAMETER; + } + + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pInfo->pDpWin) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGet(idScreen); + if (CrFbIsUpdating(hFb)) + { + WARN(("trying to update viewport while framebuffer is being updated")); + return VERR_INVALID_STATE; + } + + int rc = pInfo->pDpWin->UpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + pInfo->pDpWin->setViewportRect(&cr_server.screenVieport[idScreen].Rect); + pInfo->pDpWin->UpdateEnd(hFb); + } + else + WARN(("UpdateBegin failed %d", rc)); + } + + return VINF_SUCCESS; +} + +int CrPMgrModeModify(HCR_FRAMEBUFFER hFb, uint32_t u32ModeAdd, uint32_t u32ModeRemove) +{ + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + u32ModeRemove = ((u32ModeRemove | crPMgrModeAdjustVal(u32ModeRemove)) & CR_PMGR_MODE_ALL); + u32ModeAdd = crPMgrModeAdjustVal(u32ModeAdd); + u32ModeRemove &= pInfo->u32Mode; + u32ModeAdd &= ~(u32ModeRemove | pInfo->u32Mode); + uint32_t u32ModeResulting = ((pInfo->u32Mode | u32ModeAdd) & ~u32ModeRemove); + uint32_t u32Tmp = crPMgrModeAdjustVal(u32ModeResulting); + if (u32Tmp != u32ModeResulting) + { + u32ModeAdd |= (u32Tmp & ~u32ModeResulting); + u32ModeRemove |= (~u32Tmp & u32ModeResulting); + u32ModeResulting = u32Tmp; + Assert(u32ModeResulting == ((pInfo->u32Mode | u32ModeAdd) & ~u32ModeRemove)); + } + if (!u32ModeRemove && !u32ModeAdd) + return VINF_SUCCESS; + + if (!pInfo->pDpComposite) + { + pInfo->pDpComposite = new CrFbDisplayComposite(); + pInfo->pDpComposite->setFramebuffer(hFb); + } + + CrFbWindow * pOldWin = NULL; + + if (u32ModeRemove & CR_PMGR_MODE_ROOTVR) + { + CRASSERT(pInfo->pDpWinRootVr); + CRASSERT(pInfo->pDpWin == pInfo->pDpWinRootVr); + pInfo->pDpComposite->remove(pInfo->pDpWinRootVr); + pOldWin = pInfo->pDpWinRootVr->windowDetach(); + CRASSERT(pOldWin); + delete pInfo->pDpWinRootVr; + pInfo->pDpWinRootVr = NULL; + pInfo->pDpWin = NULL; + } + else if (u32ModeRemove & CR_PMGR_MODE_WINDOW) + { + CRASSERT(!pInfo->pDpWinRootVr); + CRASSERT(pInfo->pDpWin); + pInfo->pDpComposite->remove(pInfo->pDpWin); + pOldWin = pInfo->pDpWin->windowDetach(); + CRASSERT(pOldWin); + delete pInfo->pDpWin; + pInfo->pDpWin = NULL; + } + + if (u32ModeRemove & CR_PMGR_MODE_VRDP) + { + CRASSERT(pInfo->pDpVrdp); + if (pInfo->pDpComposite) + pInfo->pDpComposite->remove(pInfo->pDpVrdp); + else + CrFbDisplaySet(hFb, NULL); + + delete pInfo->pDpVrdp; + pInfo->pDpVrdp = NULL; + } + + CrFbDisplayBase *pDpToSet = NULL; + + if (u32ModeAdd & CR_PMGR_MODE_ROOTVR) + { + CRASSERT(!pInfo->pDpWin); + CRASSERT(!pInfo->pDpWinRootVr); + + if (!pOldWin) + pOldWin = new CrFbWindow(cr_server.screen[idScreen].winID); + + pInfo->pDpWinRootVr = new CrFbDisplayWindowRootVr(pOldWin, &cr_server.screenVieport[idScreen].Rect); + pOldWin = NULL; + pInfo->pDpWin = pInfo->pDpWinRootVr; + pInfo->pDpComposite->add(pInfo->pDpWinRootVr); + } + else if (u32ModeAdd & CR_PMGR_MODE_WINDOW) + { + CRASSERT(!pInfo->pDpWin); + CRASSERT(!pInfo->pDpWinRootVr); + + if (!pOldWin) + pOldWin = new CrFbWindow(cr_server.screen[idScreen].winID); + + pInfo->pDpWin = new CrFbDisplayWindow(pOldWin, &cr_server.screenVieport[idScreen].Rect); + pOldWin = NULL; + pInfo->pDpComposite->add(pInfo->pDpWin); + } + + if (u32ModeAdd & CR_PMGR_MODE_VRDP) + { + CRASSERT(!pInfo->pDpVrdp); + pInfo->pDpVrdp = new CrFbDisplayVrdp(); + pInfo->pDpComposite->add(pInfo->pDpVrdp); + } + + if (pInfo->pDpComposite->getDisplayCount() > 1) + { + ICrFbDisplay* pCur = CrFbDisplayGet(hFb); + if (pCur != (ICrFbDisplay*)pInfo->pDpComposite) + CrFbDisplaySet(hFb, pInfo->pDpComposite); + } + else + { + ICrFbDisplay* pCur = CrFbDisplayGet(hFb); + ICrFbDisplay* pFirst = pInfo->pDpComposite->first(); + if (pCur != pFirst) + CrFbDisplaySet(hFb, pFirst); + } + + if (pOldWin) + delete pOldWin; + + pInfo->u32Mode = u32ModeResulting; + + return VINF_SUCCESS; +} + +static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove) +{ + g_CrPresenter.u32DisplayMode = (g_CrPresenter.u32DisplayMode | u32ModeAdd) & ~u32ModeRemove; + + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + CrPMgrModeModify(hFb, u32ModeAdd, u32ModeRemove); + } + + return VINF_SUCCESS; +} + +int CrPMgrModeVrdp(bool fEnable) +{ + uint32_t u32ModeAdd, u32ModeRemove; + if (fEnable) + { + u32ModeAdd = CR_PMGR_MODE_VRDP; + u32ModeRemove = 0; + } + else + { + u32ModeAdd = 0; + u32ModeRemove = CR_PMGR_MODE_VRDP; + } + return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove); +} + +int CrPMgrModeRootVr(bool fEnable) +{ + uint32_t u32ModeAdd, u32ModeRemove; + if (fEnable) + { + u32ModeAdd = CR_PMGR_MODE_ROOTVR; + u32ModeRemove = CR_PMGR_MODE_WINDOW; + } + else + { + u32ModeAdd = CR_PMGR_MODE_WINDOW; + u32ModeRemove = CR_PMGR_MODE_ROOTVR; + } + + return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove); +} + +int CrPMgrModeWinVisible(bool fEnable) +{ + if (!g_CrPresenter.fWindowsForceHidden == !!fEnable) + return VINF_SUCCESS; + + g_CrPresenter.fWindowsForceHidden = !fEnable; + + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + + if (pInfo->pDpWin) + pInfo->pDpWin->winVisibilityChanged(); + } + + return VINF_SUCCESS; +} + +int CrPMgrRootVrUpdate() +{ + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FBDISPLAY_INFO *pInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + pInfo->pDpWinRootVr->RegionsChanged(hFb); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + + return VINF_SUCCESS; +} + +/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */ +int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap) +{ + CrFBmInit(pMap); + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("UpdateBegin failed, rc %d", rc)); + for (HCR_FRAMEBUFFER hTmpFb = CrPMgrFbGetFirstEnabled(); + hFb != hTmpFb; + hTmpFb = CrPMgrFbGetNextEnabled(hTmpFb)) + { + CrFbUpdateEnd(hTmpFb); + CrFBmClear(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex); + } + return rc; + } + + CrFBmSet(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex); + } + + return VINF_SUCCESS; +} + +/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */ +void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap) +{ + for (uint32_t i = 0; i < (uint32_t)cr_server.screenCount; ++i) + { + if (!CrFBmIsSet(pMap, i)) + continue; + + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i); + CRASSERT(hFb); + CrFbUpdateEnd(hFb); + } +} + +/*client should notify the manager about the framebuffer resize via this function */ +int CrPMgrNotifyResize(HCR_FRAMEBUFFER hFb) +{ + int rc = VINF_SUCCESS; + if (CrFbIsEnabled(hFb)) + { + rc = CrPMgrModeModify(hFb, g_CrPresenter.u32DisplayMode, 0); + if (!RT_SUCCESS(rc)) + { + WARN(("CrPMgrModeModify failed rc %d", rc)); + return rc; + } + } + else + { + rc = CrPMgrModeModify(hFb, 0, CR_PMGR_MODE_ALL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrPMgrModeModify failed rc %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +int CrFbEntrySaveState(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY *hEntry, PSSMHANDLE pSSM) +{ + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + int rc = SSMR3PutU32(pSSM, pFbTex->pTobj->id); + AssertRCReturn(rc, rc); + uint32_t u32 = 0; + + u32 = CrVrScrCompositorEntryFlagsGet(pEntry); + rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + const RTRECT *pRect = CrVrScrCompositorEntryRectGet(pEntry); + + rc = SSMR3PutS32(pSSM, pRect->xLeft); + AssertRCReturn(rc, rc); + rc = SSMR3PutS32(pSSM, pRect->yTop); + AssertRCReturn(rc, rc); +#if 0 + rc = SSMR3PutS32(pSSM, pRect->xRight); + AssertRCReturn(rc, rc); + rc = SSMR3PutS32(pSSM, pRect->yBottom); + AssertRCReturn(rc, rc); +#endif + + rc = CrVrScrCompositorEntryRegionsGet(&pFb->Compositor, pEntry, &u32, NULL, NULL, &pRect); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + if (u32) + { + rc = SSMR3PutMem(pSSM, pRect, u32 * sizeof (*pRect)); + AssertRCReturn(rc, rc); + } + return rc; +} + +int CrFbSaveState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + uint32_t u32 = 0; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CRASSERT(pTexData); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + if (pFbTex->pTobj) + ++u32; + } + + int rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter); + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + if (pFbTex->pTobj) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + rc = CrFbEntrySaveState(pFb, hEntry, pSSM); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + +int CrPMgrSaveState(PSSMHANDLE pSSM) +{ + int rc; + int cDisplays = 0, i; + for (i = 0; i < cr_server.screenCount; ++i) + { + if (CrPMgrFbGetEnabled(i)) + ++cDisplays; + } + + rc = SSMR3PutS32(pSSM, cDisplays); + AssertRCReturn(rc, rc); + + if (!cDisplays) + return VINF_SUCCESS; + + rc = SSMR3PutS32(pSSM, cr_server.screenCount); + AssertRCReturn(rc, rc); + + for (i = 0; i < cr_server.screenCount; ++i) + { + CR_FRAMEBUFFER *hFb = CrPMgrFbGetEnabled(i); + if (hFb) + { + Assert(hFb->ScreenInfo.u32ViewIndex == i); + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32ViewIndex); + AssertRCReturn(rc, rc); + + rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginX); + AssertRCReturn(rc, rc); + + rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginY); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32LineSize); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Width); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Height); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16BitsPerPixel); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16Flags); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, (uint32_t)(((uintptr_t)hFb->pvVram) - ((uintptr_t)g_pvVRamBase))); + AssertRCReturn(rc, rc); + + rc = CrFbSaveState(hFb, pSSM); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + +int CrFbEntryLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version) +{ + uint32_t texture; + int rc = SSMR3GetU32(pSSM, &texture); + AssertRCReturn(rc, rc); + + uint32_t fFlags; + rc = SSMR3GetU32(pSSM, &fFlags); + AssertRCReturn(rc, rc); + + + HCR_FRAMEBUFFER_ENTRY hEntry; + + rc = CrFbEntryCreateForTexId(pFb, texture, fFlags, &hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexId Failed")); + return rc; + } + + Assert(hEntry); + + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + + RTPOINT Point; + rc = SSMR3GetS32(pSSM, &Point.x); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &Point.y); + AssertRCReturn(rc, rc); + + uint32_t cRects; + rc = SSMR3GetU32(pSSM, &cRects); + AssertRCReturn(rc, rc); + + RTRECT * pRects = NULL; + if (cRects) + { + pRects = (RTRECT *)crAlloc(cRects * sizeof (*pRects)); + AssertReturn(pRects, VERR_NO_MEMORY); + + rc = SSMR3GetMem(pSSM, pRects, cRects * sizeof (*pRects)); + AssertRCReturn(rc, rc); + } + + rc = CrFbEntryRegionsSet(pFb, hEntry, &Point, cRects, pRects, false); + AssertRCReturn(rc, rc); + + if (pRects) + crFree(pRects); + + CrFbEntryRelease(pFb, hEntry); + + return VINF_SUCCESS; +} + +int CrFbLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version) +{ + uint32_t u32 = 0; + int rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + + if (!u32) + return VINF_SUCCESS; + + rc = CrFbUpdateBegin(pFb); + AssertRCReturn(rc, rc); + + for (uint32_t i = 0; i < u32; ++i) + { + rc = CrFbEntryLoadState(pFb, pSSM, version); + AssertRCReturn(rc, rc); + + } + + CrFbUpdateEnd(pFb); + + return VINF_SUCCESS; +} + +int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version) +{ + int rc; + int cDisplays, screenCount, i; + + rc = SSMR3GetS32(pSSM, &cDisplays); + AssertRCReturn(rc, rc); + + if (!cDisplays) + return VINF_SUCCESS; + + rc = SSMR3GetS32(pSSM, &screenCount); + AssertRCReturn(rc, rc); + + CRASSERT(screenCount == cr_server.screenCount); + + CRScreenInfo screen[CR_MAX_GUEST_MONITORS]; + + if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO) + { + for (i = 0; i < cr_server.screenCount; ++i) + { + rc = SSMR3GetS32(pSSM, &screen[i].x); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &screen[i].y); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &screen[i].w); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &screen[i].h); + AssertRCReturn(rc, rc); + } + } + + for (i = 0; i < cDisplays; ++i) + { + int iScreen; + + rc = SSMR3GetS32(pSSM, &iScreen); + AssertRCReturn(rc, rc); + + CR_FRAMEBUFFER *pFb = CrPMgrFbGet(iScreen); + Assert(pFb); + + rc = CrFbUpdateBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbUpdateBegin failed %d", rc)); + return rc; + } + + VBVAINFOSCREEN Screen; + void *pvVRAM; + + Screen.u32ViewIndex = iScreen; + + if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO) + { + memset(&Screen, 0, sizeof (Screen)); + Screen.u32LineSize = 4 * screen[iScreen].w; + Screen.u32Width = screen[iScreen].w; + Screen.u32Height = screen[iScreen].h; + Screen.u16BitsPerPixel = 4; + Screen.u16Flags = VBVA_SCREEN_F_ACTIVE; + + pvVRAM = g_pvVRamBase; + } + else + { + rc = SSMR3GetS32(pSSM, &Screen.i32OriginX); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &Screen.i32OriginY); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32LineSize); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32Width); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32Height); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU16(pSSM, &Screen.u16BitsPerPixel); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU16(pSSM, &Screen.u16Flags); + AssertRCReturn(rc, rc); + + uint32_t offVram = 0; + rc = SSMR3GetU32(pSSM, &offVram); + AssertRCReturn(rc, rc); + + pvVRAM = (void*)(((uintptr_t)g_pvVRamBase) + offVram); + } + + crVBoxServerMuralFbResizeBegin(pFb); + + rc = CrFbResize(pFb, &Screen, pvVRAM); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbResize failed %d", rc)); + return rc; + } + + rc = CrFbLoadState(pFb, pSSM, version); + AssertRCReturn(rc, rc); + + crVBoxServerMuralFbResizeEnd(pFb); + + CrFbUpdateEnd(pFb); + + CrPMgrNotifyResize(pFb); + } + + return VINF_SUCCESS; +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects) +{ + uint32_t idScreen = CR_PRESENT_GET_SCREEN(cfg); + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("Invalid guest screen")); + return; + } + + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idScreen); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + return; + } + + HCR_FRAMEBUFFER_ENTRY hEntry; + int rc; + if (texture) + { + rc = CrFbEntryCreateForTexId(hFb, texture, (cfg & CR_PRESENT_FLAG_TEX_NONINVERT_YCOORD) ? 0 : CRBLT_F_INVERT_SRC_YCOORDS, &hEntry); + if (!RT_SUCCESS(rc)) + { + LOG(("CrFbEntryCreateForTexId Failed")); + return; + } + + Assert(hEntry); + +#if 0 + if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS)) + { + CR_SERVER_DUMP_TEXPRESENT(&pEntry->CEntry.Tex); + } +#endif + } + else + hEntry = NULL; + + rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS)) + { + RTPOINT Point = {xPos, yPos}; + rc = CrFbEntryRegionsAdd(hFb, hEntry, &Point, (uint32_t)cRects, (const RTRECT*)pRects, false); + } + else + { + CrFbRegionsClear(hFb); + } + + CrFbUpdateEnd(hFb); + } + else + { + WARN(("CrFbUpdateBegin Failed")); + } + + if (hEntry) + CrFbEntryRelease(hFb, hEntry); +} + +DECLINLINE(void) crVBoxPRectUnpack(const VBOXCMDVBVA_RECT *pVbvaRect, RTRECT *pRect) +{ + pRect->xLeft = pVbvaRect->xLeft; + pRect->yTop = pVbvaRect->yTop; + pRect->xRight = pVbvaRect->xRight; + pRect->yBottom = pVbvaRect->yBottom; +} + +DECLINLINE(void) crVBoxPRectUnpacks(const VBOXCMDVBVA_RECT *paVbvaRects, RTRECT *paRects, uint32_t cRects) +{ + uint32_t i = 0; + for (; i < cRects; ++i) + { + crVBoxPRectUnpack(&paVbvaRects[i], &paRects[i]); + } +} + +int32_t crVBoxServerCrCmdBltProcess(PVBOXCMDVBVA_HDR pCmd, uint32_t cbCmd) +{ + uint8_t u8Flags = pCmd->u8Flags; + if (u8Flags & (VBOXCMDVBVA_OPF_ALLOC_DSTPRIMARY | VBOXCMDVBVA_OPF_ALLOC_SRCPRIMARY)) + { + VBOXCMDVBVA_BLT_PRIMARY *pBlt = (VBOXCMDVBVA_BLT_PRIMARY*)pCmd; + uint8_t u8PrimaryID = pBlt->Hdr.u.u8PrimaryID; + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u8PrimaryID); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + + const VBOXCMDVBVA_RECT *pPRects = pBlt->aRects; + uint32_t cRects = (cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) / sizeof (VBOXCMDVBVA_RECT); + RTRECT *pRects; + if (g_CrPresenter.cbTmpBuf < cRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf) + RTMemFree(g_CrPresenter.pvTmpBuf); + + g_CrPresenter.cbTmpBuf = (cRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf = RTMemAlloc(g_CrPresenter.cbTmpBuf); + if (!g_CrPresenter.pvTmpBuf) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf = 0; + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + } + + pRects = (RTRECT *)g_CrPresenter.pvTmpBuf; + + crVBoxPRectUnpacks(pPRects, pRects, cRects); + + Assert(!((cbCmd - RT_OFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) % sizeof (VBOXCMDVBVA_RECT))); + + if (u8Flags & VBOXCMDVBVA_OPF_ALLOC_DSTPRIMARY) + { + if (!(u8Flags & VBOXCMDVBVA_OPF_ALLOC_SRCPRIMARY)) + { + /* blit to primary from non-primary */ + uint32_t texId; + if (u8Flags & VBOXCMDVBVA_OPF_ALLOC_DSTID) + { + /* TexPresent */ + texId = pBlt->alloc.u.id; + } + else + { + VBOXCMDVBVAOFFSET offVRAM = pBlt->alloc.u.offVRAM; + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + uint32_t cbScreen = pScreen->u32LineSize * pScreen->u32Height; + if (offVRAM >= g_cbVRam + || offVRAM + cbScreen >= g_cbVRam) + { + WARN(("invalid param")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + + uint8_t *pu8Buf = g_pvVRamBase + offVRAM; + texId = 0; +// cr_server.CrCmdClientInfo.pfnCltScrUpdateBegin(cr_server.CrCmdClientInfo.hCltScr); + /*todo: notify VGA device to perform updates */ + } + + crServerDispatchVBoxTexPresent(texId, u8PrimaryID, pBlt->Pos.x, pBlt->Pos.y, cRects, (const GLint*)pRects); + } + else + { + /* blit from one primary to another primary, wow */ + WARN(("not implemented")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + } + else + { + Assert(u8Flags & VBOXCMDVBVA_OPF_ALLOC_SRCPRIMARY); + /* blit from primary to non-primary */ + if (u8Flags & VBOXCMDVBVA_OPF_ALLOC_DSTID) + { + uint32_t texId = pBlt->alloc.u.id; + WARN(("not implemented")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + else + { + VBOXCMDVBVAOFFSET offVRAM = pBlt->alloc.u.offVRAM; + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + uint32_t cbScreen = pScreen->u32LineSize * pScreen->u32Height; + if (offVRAM >= g_cbVRam + || offVRAM + cbScreen >= g_cbVRam) + { + WARN(("invalid param")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + + uint8_t *pu8Buf = g_pvVRamBase + offVRAM; + + RTRECT SrcRect; + SrcRect.xLeft = 0; + SrcRect.yTop = 0; + SrcRect.xRight = pScreen->u32Width; + SrcRect.yBottom = pScreen->u32Height; + RTRECT DstRect; + DstRect.xLeft = pBlt->Pos.x; + DstRect.yTop = pBlt->Pos.y; + DstRect.xRight = DstRect.xLeft + pScreen->u32Width; + DstRect.yBottom = DstRect.yTop + pScreen->u32Height; + CR_BLITTER_IMG Img; + crFbImgFromScreenVram(pScreen, pu8Buf, &Img); + int rc = CrFbBltGetContents(hFb, &SrcRect, &DstRect, cRects, pRects, &Img); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbBltGetContents failed %d", rc)); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + } + } + } + else + { + WARN(("not implemented")); + pCmd->u.i8Result = -1; + return VINF_SUCCESS; + } + + pCmd->u.i8Result = 0; + return VINF_SUCCESS; +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py index 69d09b60..11f2e77f 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py @@ -35,7 +35,7 @@ void crServerReturnValue( const void *payload, unsigned int payload_len ) rb = (CRMessageReadback *) crAlloc( msg_len ); rb->header.type = CR_MESSAGE_READBACK; - CRDBGPTR_PRINTWB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr); + CRDBGPTR_PRINTRB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr); CRDBGPTR_CHECKNZ(&cr_server.writeback_ptr); CRDBGPTR_CHECKNZ(&cr_server.return_ptr); crMemcpy( &(rb->writeback_ptr), &(cr_server.writeback_ptr), sizeof( rb->writeback_ptr ) ); diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp new file mode 100644 index 00000000..51c5d46f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp @@ -0,0 +1,755 @@ +/* $Id: server_rpw.cpp $ */ + +/** @file + * VBox crOpenGL: Read Pixels worker + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#include "server.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_vreg.h" +#include "render/renderspu.h" + +static void crServerRpwWorkerGpuSubmit(PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry; + RTListForEach(pWorkList, pCurEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry) + { + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, CR_SERVER_RPW_ENTRY_TEX(pCurEntry, Worker)); + + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_CUR(pCurEntry)); + /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/ + cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + CR_SERVER_RPW_ENTRY_PBO_FLIP(pCurEntry); + } + else + { + void *pvData = crAlloc(4*pCurEntry->Size.cx*pCurEntry->Size.cy); + if (pvData) + { + cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pvData); + + pCurEntry->pfnData(pCurEntry, pvData); + + crFree(pvData); + } + else + { + crWarning("crAlloc failed"); + } + } + + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, 0); + } +} + +static void crServerRpwWorkerGpuComplete(PRTLISTNODE pGpuSubmitedList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry; + RTListForEach(pGpuSubmitedList, pCurEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry) + { + Assert(CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)); + + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_COMPLETED(pCurEntry)); + + void *pvData = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); + + pCurEntry->pfnData(pCurEntry, pvData); + + cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + + cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, pCurEntry->Size.cx*pCurEntry->Size.cy*4, 0, GL_STREAM_READ_ARB); + + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } +} + +static void crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(PRTLISTNODE pGpuSubmitedList, PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(pGpuSubmitedList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry) + { + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Gpu); + RTListNodeRemove(&pCurEntry->GpuSubmittedEntry); + } + + Assert(RTListIsEmpty(pGpuSubmitedList)); + + RTListForEachSafe(pWorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry) + { + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Worker)); + RTListNodeRemove(&pCurEntry->WorkerWorkEntry); + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)) + { + /* PBO mode, put to the GPU submitted queue*/ + RTListAppend(pGpuSubmitedList, &pCurEntry->GpuSubmittedEntry); + CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Worker, Gpu); + } + else + { + /* no PBO, we are already done entry data processing, free it right away */ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Gpu)); + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Worker); + } + } +} + +static void crServerRpwWorkerGetWorkLocked(CR_SERVER_RPW *pWorker, PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry) + { + RTListNodeRemove(&pCurEntry->WorkEntry); + RTListAppend(pWorkList, &pCurEntry->WorkerWorkEntry); + CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Submitted, Worker); + } +} + +static DECLCALLBACK(int) crServerRpwWorkerThread(RTTHREAD ThreadSelf, void *pvUser) +{ + CR_SERVER_RPW *pWorker = (CR_SERVER_RPW *)pvUser; + RTMSINTERVAL cWaitMillis = RT_INDEFINITE_WAIT; + RTLISTNODE WorkList, GpuSubmittedList; + CR_SERVER_RPW_CTL_TYPE enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + CR_SERVER_RPW_ENTRY *pCtlEntry = NULL; + CRMuralInfo *pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits); + bool fExit = false; + bool fForceComplete = false; + bool fNotifyCmdCompleted = false; + + CRASSERT(pDummyMural); + + int rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventSignal failed rc %d", rc); + return rc; + } + + RTListInit(&WorkList); + RTListInit(&GpuSubmittedList); + + cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId); + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + goto end; + } + + for (;;) + { + /* the crit sect is locked here */ + + if (pWorker->Ctl.enmType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED) + { + enmCtlType = pWorker->Ctl.enmType; + pCtlEntry = pWorker->Ctl.pEntry; + pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pWorker->Ctl.pEntry = NULL; + } + + crServerRpwWorkerGetWorkLocked(pWorker, &WorkList); + + RTCritSectLeave(&pWorker->CritSect); + + if (enmCtlType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED) + { + switch (enmCtlType) + { + case CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE: + break; + case CR_SERVER_RPW_CTL_TYPE_TERM: + fExit = true; + break; + default: + crWarning("unexpected CtlType %d", enmCtlType); + break; + } + enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pCtlEntry = NULL; + fNotifyCmdCompleted = true; + } + + bool fNewItems = !RTListIsEmpty(&WorkList); + bool fCompleted = false; + + if (fNewItems) + { + crServerRpwWorkerGpuSubmit(&WorkList); + } + + if (!RTListIsEmpty(&GpuSubmittedList)) + { + if (fForceComplete || fNewItems) + { + crServerRpwWorkerGpuComplete(&GpuSubmittedList); + fForceComplete = false; + fCompleted = true; + } + } + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + break; + } + + /* fNewGpuItems means new entries arrived. WorkList contains new GPU submitted data + * fCompleted means completion was performed, GpuSubmittedList contains old GPU submitted data, + * which is now completed and should be released */ + if (fNewItems || fCompleted) + { + crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(&GpuSubmittedList, &WorkList); + } + + if (fExit || !fNewItems) + { + RTCritSectLeave(&pWorker->CritSect); + + if (fNotifyCmdCompleted) + { + rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventSignal failed rc %d", rc); + break; + } + fNotifyCmdCompleted = false; + } + + if (fExit) + break; + + if (!RTListIsEmpty(&GpuSubmittedList)) + cWaitMillis = 17; /* ~60Hz */ + else + cWaitMillis = RT_INDEFINITE_WAIT; + + rc = RTSemEventWait(pWorker->hSubmitEvent, cWaitMillis); + if (!RT_SUCCESS(rc) && rc != VERR_TIMEOUT) + { + crWarning("RTSemEventWait failed, rc %d", rc); + break; + } + + if (rc == VERR_TIMEOUT) + { + Assert(!RTListIsEmpty(&GpuSubmittedList)); + fForceComplete = true; + } + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + break; + } + } + } + +end: + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + return rc; +} + +static int crServerRpwCtlNotify(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = RTSemEventSignal(pWorker->hSubmitEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = pWorker->Ctl.rc; + if (!RT_SUCCESS(rc)) + { + crWarning("WdCtl command failed rc %d", rc); + } + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + } + else + { + int tmpRc; + crWarning("RTSemEventSignal failed rc %d", rc); + tmpRc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(tmpRc)) + { + pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pWorker->Ctl.pEntry = NULL; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTSemEventSignal failed tmpRc %d", tmpRc); + } + } + + return rc; +} + +static int crServerRpwCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_CTL_TYPE enmType, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + pWorker->Ctl.enmType = enmType; + pWorker->Ctl.pEntry = pEntry; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + rc = crServerRpwCtlNotify(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlNotify failed rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +int crServerRpwInit(CR_SERVER_RPW *pWorker) +{ + int rc; + + memset(pWorker, 0, sizeof (*pWorker)); + + RTListInit(&pWorker->WorkList); + + rc = RTCritSectInit(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pWorker->hSubmitEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pWorker->Ctl.hCompleteEvent); + if (RT_SUCCESS(rc)) + { + CRASSERT(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CRASSERT(cr_server.MainContextInfo.SpuContext); + + pWorker->ctxId = cr_server.head_spu->dispatch_table.CreateContext("", cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); + if (pWorker->ctxId) + { + CRMuralInfo *pDummyMural; + pWorker->ctxVisBits = cr_server.MainContextInfo.CreateInfo.realVisualBits; + pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits); + if (pDummyMural) + { + /* since CreateContext does not actually create it on some platforms, e.g. on win, + * we need to do MakeCurrent to ensure it is created. + * There is some black magic in doing that to work around ogl driver bugs + * (i.e. we need to switch offscreen rendering off before doing make current) */ + CR_SERVER_CTX_SWITCH CtxSwitch; + + crServerCtxSwitchPrepare(&CtxSwitch, NULL); + + cr_server.head_spu->dispatch_table.Flush(); + + cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId); + + if (cr_server.currentCtxInfo) + { + CRASSERT(cr_server.currentMural); + cr_server.head_spu->dispatch_table.MakeCurrent(cr_server.currentMural->spuWindow, 0, + cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext); + } + else + cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + + crServerCtxSwitchPostprocess(&CtxSwitch); + + rc = RTThreadCreate(&pWorker->hThread, crServerRpwWorkerThread, pWorker, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CrServerDw"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + return VINF_SUCCESS; + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + } + else + { + crWarning("RTThreadCreate failed rc %d", rc); + } + } + else + { + crWarning("Failed to get dummy mural"); + rc = VERR_GENERAL_FAILURE; + } + cr_server.head_spu->dispatch_table.DestroyContext(pWorker->ctxId); + } + else + { + crWarning("CreateContext failed rc %d", rc); + } + + RTSemEventDestroy(pWorker->Ctl.hCompleteEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + RTSemEventDestroy(pWorker->hSubmitEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + + RTCritSectDelete(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectInit failed rc %d", rc); + } + + return rc; +} + +int crServerRpwEntryResizeCleaned(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height) +{ + CRContext *pContext; + if (!width || !height) + { + return VINF_SUCCESS; + } + + if (!cr_server.currentCtxInfo) + { + CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pDummy) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + + crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo); + } + + Assert(width); + Assert(height); + + pContext = cr_server.currentCtxInfo->pContext; + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } + + for (int i = 0; i < 4; ++i) + { + cr_server.head_spu->dispatch_table.GenTextures(1, &pEntry->aidWorkerTexs[i]); + + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, pEntry->aidWorkerTexs[i]); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + cr_server.head_spu->dispatch_table.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + + pEntry->iTexDraw = -pEntry->iTexDraw; + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pContext->bufferobject.unpackBuffer->hwid); + } + + if (cr_server.bUsePBOForReadback) + { + for (int i = 0; i < 2; ++i) + { + cr_server.head_spu->dispatch_table.GenBuffersARB(1, &pEntry->aidPBOs[i]); + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pEntry->aidPBOs[i]); + cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, width*height*4, 0, GL_STREAM_READ_ARB); + } + + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pContext->bufferobject.packBuffer->hwid); + } + else + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + pEntry->iCurPBO = 0; + } + + + GLuint uid = pContext->texture.unit[pContext->texture.curTextureUnit].currentTexture2D->hwid; + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, uid); + + + pEntry->Size.cx = width; + pEntry->Size.cy = height; + + crServerRpwEntryDbgVerify(pEntry); + + return VINF_SUCCESS; +} + +int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + if (!pEntry->Size.cx) + return VINF_SUCCESS; + + int rc = crServerRpwEntryCancel(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryCancel failed rc %d", rc); + return rc; + } + + if (!cr_server.currentCtxInfo) + { + CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pDummy) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + + crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo); + } + + cr_server.head_spu->dispatch_table.DeleteTextures(4, pEntry->aidWorkerTexs); + + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pEntry)) + { + cr_server.head_spu->dispatch_table.DeleteBuffersARB(2, pEntry->aidPBOs); + memset(pEntry->aidPBOs, 0, sizeof (pEntry->aidPBOs)); + pEntry->iCurPBO = -1; + } + + memset(pEntry->aidWorkerTexs, 0, sizeof (pEntry->aidWorkerTexs)); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu); + pEntry->iTexDraw = -1; + pEntry->iTexSubmitted = -2; + pEntry->iTexWorker = -3; + pEntry->iTexGpu = -4; + pEntry->Size.cx = 0; + pEntry->Size.cy = 0; + return VINF_SUCCESS; +} + +int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height) +{ + if (!width || !height) + { + width = 0; + height = 0; + } + + if (width == pEntry->Size.cx && width == pEntry->Size.cy) + return VINF_SUCCESS; + + int rc = crServerRpwEntryCleanup(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryCleanup failed rc %d", rc); + return rc; + } + + rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc); + } + return rc; +} + +int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData) +{ + memset(pEntry, 0, sizeof (*pEntry)); + + pEntry->iTexDraw = -1; + pEntry->iTexSubmitted = -2; + pEntry->iTexWorker = -3; + pEntry->iTexGpu = -4; + pEntry->iCurPBO = -1; + pEntry->pfnData = pfnData; + int rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)) + { + crWarning("submitting empty entry, ignoting"); + Assert(!pEntry->Size.cx); + Assert(!pEntry->Size.cy); + return VERR_INVALID_PARAMETER; + } + + Assert(pEntry->Size.cx); + Assert(pEntry->Size.cy); + + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + Assert(pWorker->Ctl.enmType == CR_SERVER_RPW_CTL_TYPE_UNDEFINED); + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted)) + { + CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(pEntry, Draw, Submitted); + RTListAppend(&pWorker->WorkList, &pEntry->WorkEntry); + } + else + { + CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(pEntry, Draw, Submitted); + } + RTCritSectLeave(&pWorker->CritSect); + + RTSemEventSignal(pWorker->hSubmitEvent); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + return rc; +} + +static int crServerRpwEntryCancelCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, CR_SERVER_RPW_CTL_TYPE enmType) +{ + if (CR_SERVER_RPW_CTL_TYPE_TERM == enmType && pEntry) + { + crWarning("Entry should be null for term request"); + pEntry = NULL; + } + + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + if (pEntry) + { + if (CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted)) + { + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted); + RTListNodeRemove(&pEntry->WorkEntry); + } + + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker) && !CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu)) + { + /* can cancel it wight away */ + RTCritSectLeave(&pWorker->CritSect); + return VINF_SUCCESS; + } + } + else + { + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry) + { + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Submitted); + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted); + RTListNodeRemove(&pCurEntry->WorkEntry); + } + } + pWorker->Ctl.enmType = enmType; + pWorker->Ctl.pEntry = pEntry; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + rc = crServerRpwCtlNotify(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlNotify failed rc %d", rc); + } + return VINF_SUCCESS; +} + +int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = crServerRpwCtl(pWorker, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtl failed rc %d", rc); + } + return rc; +} + +int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + return crServerRpwEntryCancelCtl(pWorker, pEntry, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE); +} + +static int crServerRpwCtlTerm(CR_SERVER_RPW *pWorker) +{ + int rc = crServerRpwEntryCancelCtl(pWorker, NULL, CR_SERVER_RPW_CTL_TYPE_TERM); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtl failed rc %d", rc); + } + return rc; +} + +int crServerRpwTerm(CR_SERVER_RPW *pWorker) +{ + int rc = crServerRpwCtlTerm(pWorker); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlTerm failed rc %d", rc); + return rc; + } + + rc = RTThreadWait(pWorker->hThread, RT_INDEFINITE_WAIT, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("RTThreadWait failed rc %d", rc); + return rc; + } + + RTSemEventDestroy(pWorker->Ctl.hCompleteEvent); + RTSemEventDestroy(pWorker->hSubmitEvent); + RTCritSectDelete(&pWorker->CritSect); + + return VINF_SUCCESS; +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py index 363ccb43..1698841f 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py @@ -74,31 +74,37 @@ for index in range(len(funcs)): { GLuint fboid; CRASSERT(tablesize/sizeof(%s)==1); - fboid = crStateFBOHWIDtoID((GLuint) *get_values); - if (cr_server.curClient->currentMural->bUseFBO - && crServerIsRedirectedToFBO() - && fboid==cr_server.curClient->currentMural->idFBO) + fboid = (GLuint) *get_values; + if (crServerIsRedirectedToFBO() + && (fboid==cr_server.curClient->currentMural->aidFBOs[0] + || fboid==cr_server.curClient->currentMural->aidFBOs[1])) { fboid = 0; } + else + { + fboid = crStateFBOHWIDtoID(fboid); + } *get_values = (%s) fboid; } else if (GL_READ_BUFFER==pname) { - if (cr_server.curClient->currentMural->bUseFBO && crServerIsRedirectedToFBO() - && cr_server.curClient->currentMural->idFBO + if (crServerIsRedirectedToFBO() + && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurReadBuffer) && !crStateGetCurrent()->framebufferobject.readFB) { *get_values = (%s) crStateGetCurrent()->buffer.readBuffer; + Assert(crStateGetCurrent()->buffer.readBuffer == GL_BACK || crStateGetCurrent()->buffer.readBuffer == GL_FRONT); } } else if (GL_DRAW_BUFFER==pname) { - if (cr_server.curClient->currentMural->bUseFBO && crServerIsRedirectedToFBO() - && cr_server.curClient->currentMural->idFBO + if (crServerIsRedirectedToFBO() + && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurDrawBuffer) && !crStateGetCurrent()->framebufferobject.drawFB) { *get_values = (%s) crStateGetCurrent()->buffer.drawBuffer; + Assert(crStateGetCurrent()->buffer.drawBuffer == GL_BACK || crStateGetCurrent()->buffer.drawBuffer == GL_FRONT); } } else if (GL_RENDERBUFFER_BINDING_EXT==pname) @@ -132,7 +138,14 @@ for index in range(len(funcs)): *get_values = (%s)CR_MAX_TEXTURE_UNITS; } } - """ % (types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index]) + else if (GL_MAX_VERTEX_ATTRIBS_ARB==pname) + { + if (CR_MAX_VERTEX_ATTRIBS < (GLuint)*get_values) + { + *get_values = (%s)CR_MAX_VERTEX_ATTRIBS; + } + } + """ % (types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index]) print '\tcrServerReturnValue( get_values, tablesize );' print '\tcrFree(get_values);' print '}\n' diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special index 7292626a..e2df4e91 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special @@ -160,6 +160,7 @@ ProgramParameter4fNV ProgramParameter4dNV GetCompressedTexImageARB GenBuffersARB +DeleteBuffersARB GetBufferSubDataARB GetBufferPointervARB MapBufferARB @@ -248,3 +249,17 @@ BlitFramebufferEXT EndList DrawBuffer ReadBuffer +VBoxTexPresent +GetError +GetProgramiv +GetShaderiv +Begin +DrawArrays +DrawElements +End +TexEnvf +TexEnvfv +TexEnvi +TexEnviv +GetTexEnvfv +GetTexEnviv
\ No newline at end of file diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c index b5c8a5bf..29670790 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c @@ -84,7 +84,7 @@ void crServerAddToRunQueue( CRClient *client ) /* give this client a unique number if needed */ if (!client->number) { - client->number = crServerGenerateID(&cr_server.idsPool.freeClientID); + client->number = client->conn->u32ClientID; } crDebug("Adding client %p to the run queue", client); @@ -281,6 +281,28 @@ crServerDeleteClient( CRClient *client ) pNode->next = cr_server.pCleanupClient; cr_server.pCleanupClient = pNode; } + + if (!cr_server.numClients) + { + /* if no clients, the guest driver may be unloaded, + * and thus the visible regions situation might not be under control anymore, + * so cleanup the 3D framebuffer data here + * @todo: what really should happen is that guest driver on unload + * posts some request to host that would copy the current framebuffer 3D data to the 2D buffer + * (i.e. to the memory used by the standard IFramebuffer API) */ + HCR_FRAMEBUFFER hFb; + for (hFb = CrPMgrFbGetFirstEnabled(); hFb; hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hFb); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + } } /** @@ -441,7 +463,7 @@ crServerDispatchMessage(CRConnection *conn, CRMessage *msg) { uint32_t cbWriteback = pCmdData->cbWriteback; rc = crVBoxServerInternalClientRead(conn->pClient, (uint8_t*)pCmdData->pWriteback, &cbWriteback); - CRASSERT(rc == VINF_SUCCESS || rc == VERR_BUFFER_OVERFLOW); + Assert(rc == VINF_SUCCESS || rc == VERR_BUFFER_OVERFLOW); *pCmdData->pcbWriteback = cbWriteback; } VBOXCRHGSMI_CMD_CHECK_COMPLETE(pCmdData, rc); diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c index 392bcd20..8037ac66 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c @@ -136,3 +136,56 @@ CR_FUNC_IMAGE(TexImage2D, CR_FUNC_IMAGE(TexImage3D, (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid * pixels), (target, level, internalFormat, width, height, depth, border, format, type, realptr), pixels) + + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvf( GLenum target, GLenum pname, GLfloat param ) +{ + crStateTexEnvf( target, pname, param ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvf( target, pname, param );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvfv( GLenum target, GLenum pname, const GLfloat * params ) +{ + crStateTexEnvfv( target, pname, params ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvfv( target, pname, params );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvi( GLenum target, GLenum pname, GLint param ) +{ + crStateTexEnvi( target, pname, param ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvi( target, pname, param );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnviv( GLenum target, GLenum pname, const GLint * params ) +{ + crStateTexEnviv( target, pname, params ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnviv( target, pname, params );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnvfv( GLenum target, GLenum pname, GLfloat * params ) +{ + GLfloat local_params[4]; + (void) params; + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + cr_server.head_spu->dispatch_table.GetTexEnvfv( target, pname, local_params ); + else + crStateGetTexEnvfv( target, pname, local_params ); + + crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLfloat) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnviv( GLenum target, GLenum pname, GLint * params ) +{ + GLint local_params[4]; + (void) params; + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + cr_server.head_spu->dispatch_table.GetTexEnviv( target, pname, local_params ); + else + crStateGetTexEnviv( target, pname, local_params ); + + crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLint) ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c index ce380be6..69351255 100644 --- a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c @@ -10,21 +10,94 @@ #include "cr_rand.h" #include "cr_string.h" +#include "render/renderspu.h" + GLint SERVER_DISPATCH_APIENTRY crServerDispatchWindowCreate(const char *dpyName, GLint visBits) { return crServerDispatchWindowCreateEx(dpyName, visBits, -1); } +GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID) +{ + CRMuralInfo *defaultMural; + GLint dims[2]; + GLint windowID = -1; + GLint spuWindow = 0; + GLint realVisBits = visBits; + const char *dpyName = ""; -GLint -crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID) + crMemset(mural, 0, sizeof (*mural)); + + if (cr_server.fVisualBitsDefault) + realVisBits = cr_server.fVisualBitsDefault; + +#ifdef RT_OS_DARWIN + if (fGuestWindow) + { + CRMuralInfo *dummy = crServerGetDummyMural(visBits); + if (!dummy) + { + WARN(("crServerGetDummyMural failed")); + return -1; + } + spuWindow = dummy->spuWindow; + mural->fIsDummyRefference = GL_TRUE; + } + else +#endif + { + /* + * Have first SPU make a new window. + */ + spuWindow = cr_server.head_spu->dispatch_table.WindowCreate( dpyName, realVisBits ); + if (spuWindow < 0) { + return spuWindow; + } + mural->fIsDummyRefference = GL_FALSE; + } + + /* get initial window size */ + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, spuWindow, GL_INT, 2, dims); + + defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); + CRASSERT(defaultMural); + mural->gX = 0; + mural->gY = 0; + mural->width = dims[0]; + mural->height = dims[1]; + + mural->spuWindow = spuWindow; + mural->screenId = 0; + mural->fHasParentWindow = !!cr_server.screen[0].winID; + mural->bVisible = !cr_server.bWindowsInitiallyHidden; + + mural->cVisibleRects = 0; + mural->pVisibleRects = NULL; + mural->bReceivedRects = GL_FALSE; + + /* generate ID for this new window/mural (special-case for file conns) */ + if (cr_server.curClient && cr_server.curClient->conn->type == CR_FILE) + windowID = spuWindow; + else + windowID = preloadWinID<0 ? (GLint)crHashtableAllocKeys( cr_server.muralTable, 1 ) : preloadWinID; + + mural->CreateInfo.realVisualBits = realVisBits; + mural->CreateInfo.requestedVisualBits = visBits; + mural->CreateInfo.externalID = windowID; + mural->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL; + + CR_STATE_SHAREDOBJ_USAGE_INIT(mural); + + return windowID; +} + +GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID) { CRMuralInfo *mural; GLint windowID = -1; - GLint spuWindow; - GLint dims[2]; - CRCreateInfo_t *pCreateInfo; + + dpyName = ""; if (cr_server.sharedWindows) { int pos, j; @@ -54,58 +127,30 @@ crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preload } } - /* - * Have first SPU make a new window. - */ - spuWindow = cr_server.head_spu->dispatch_table.WindowCreate( dpyName, visBits ); - if (spuWindow < 0) { - crServerReturnValue( &spuWindow, sizeof(spuWindow) ); - return spuWindow; - } - - /* get initial window size */ - cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, spuWindow, GL_INT, 2, dims); /* * Create a new mural for the new window. */ mural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); - if (mural) { - CRMuralInfo *defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); - CRASSERT(defaultMural); - mural->gX = 0; - mural->gY = 0; - mural->width = dims[0]; - mural->height = dims[1]; - - mural->spuWindow = spuWindow; - mural->screenId = 0; - mural->bVisible = GL_FALSE; - mural->bUseFBO = GL_FALSE; - - mural->cVisibleRects = 0; - mural->pVisibleRects = NULL; - mural->bReceivedRects = GL_FALSE; - - mural->pvOutputRedirectInstance = NULL; - - /* generate ID for this new window/mural (special-case for file conns) */ - if (cr_server.curClient && cr_server.curClient->conn->type == CR_FILE) - windowID = spuWindow; - else - windowID = preloadWinID<0 ? crServerGenerateID(&cr_server.idsPool.freeWindowID) : preloadWinID; - crHashtableAdd(cr_server.muralTable, windowID, mural); - - pCreateInfo = (CRCreateInfo_t *) crAlloc(sizeof(CRCreateInfo_t)); - pCreateInfo->pszDpyName = dpyName ? crStrdup(dpyName) : NULL; - pCreateInfo->visualBits = visBits; - crHashtableAdd(cr_server.pWindowCreateInfoTable, windowID, pCreateInfo); + if (!mural) + { + crWarning("crCalloc failed!"); + return -1; + } - crServerSetupOutputRedirect(mural); + windowID = crServerMuralInit(mural, GL_TRUE, visBits, preloadWinID); + if (windowID < 0) + { + crWarning("crServerMuralInit failed!"); + crServerReturnValue( &windowID, sizeof(windowID) ); + crFree(mural); + return windowID; } + crHashtableAdd(cr_server.muralTable, windowID, mural); + crDebug("CRServer: client %p created new window %d (SPU window %d)", - cr_server.curClient, windowID, spuWindow); + cr_server.curClient, windowID, mural->spuWindow); if (windowID != -1 && !cr_server.bIsInLoadingState) { int pos; @@ -117,6 +162,9 @@ crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preload } } + /* ensure we have a dummy mural created right away to avoid potential deadlocks on VM shutdown */ + crServerGetDummyMural(mural->CreateInfo.realVisualBits); + crServerReturnValue( &windowID, sizeof(windowID) ); return windowID; } @@ -137,6 +185,65 @@ static int crServerRemoveClientWindow(CRClient *pClient, GLint window) return false; } +void crServerMuralTerm(CRMuralInfo *mural) +{ + PCR_BLITTER pBlitter; + crServerRedirMuralFBO(mural, false); + crServerDeleteMuralFBO(mural); + + if (cr_server.currentMural == mural) + { + CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + /* reset the current context to some dummy values to ensure render spu does not switch to a default "0" context, + * which might lead to muralFBO (offscreen rendering) gl entities being created in a scope of that context */ + cr_server.head_spu->dispatch_table.MakeCurrent(dummyMural->spuWindow, 0, cr_server.MainContextInfo.SpuContext); + cr_server.currentWindow = -1; + cr_server.currentMural = dummyMural; + } + else + { + CRASSERT(cr_server.currentWindow != mural->CreateInfo.externalID); + } + + pBlitter = crServerVBoxBlitterGetInitialized(); + if (pBlitter) + { + const CR_BLITTER_WINDOW * pWindow = CrBltMuralGetCurrentInfo(pBlitter); + if (pWindow && pWindow->Base.id == mural->spuWindow) + { + CRMuralInfo *dummy = crServerGetDummyMural(mural->CreateInfo.realVisualBits); + CR_BLITTER_WINDOW DummyInfo; + CRASSERT(dummy); + crServerVBoxBlitterWinInit(&DummyInfo, dummy); + CrBltMuralSetCurrentInfo(pBlitter, &DummyInfo); + } + } + + if (!mural->fIsDummyRefference) + cr_server.head_spu->dispatch_table.WindowDestroy( mural->spuWindow ); + + mural->spuWindow = 0; + + if (mural->pVisibleRects) + { + crFree(mural->pVisibleRects); + } + + if (mural->CreateInfo.pszDpyName) + crFree(mural->CreateInfo.pszDpyName); + + crServerRedirMuralFbClear(mural); +} + +static void crServerCleanupCtxMuralRefsCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *ctxInfo = (CRContextInfo *) data1; + CRMuralInfo *mural = (CRMuralInfo *) data2; + + if (ctxInfo->currentMural == mural) + ctxInfo->currentMural = NULL; +} + void SERVER_DISPATCH_APIENTRY crServerDispatchWindowDestroy( GLint window ) { @@ -157,22 +264,13 @@ crServerDispatchWindowDestroy( GLint window ) return; } - if (mural->pvOutputRedirectInstance) - { - cr_server.outputRedirect.CROREnd(mural->pvOutputRedirectInstance); - mural->pvOutputRedirectInstance = NULL; - } + crDebug("CRServer: Destroying window %d (spu window %d)", window, mural->spuWindow); - if (cr_server.currentWindow == window) - { - cr_server.currentWindow = -1; - } + crHashtableWalk(cr_server.contextTable, crServerCleanupCtxMuralRefsCB, mural); - crServerRedirMuralFBO(mural, GL_FALSE); - crServerDeleteMuralFBO(mural); + crServerMuralTerm(mural); - crDebug("CRServer: Destroying window %d (spu window %d)", window, mural->spuWindow); - cr_server.head_spu->dispatch_table.WindowDestroy( mural->spuWindow ); + CRASSERT(cr_server.currentWindow != window); if (cr_server.curClient) { @@ -233,13 +331,29 @@ crServerDispatchWindowDestroy( GLint window ) pNode = pNode->next; } - crHashtableDelete(cr_server.pWindowCreateInfoTable, window, crServerCreateInfoDeleteCB); + crHashtableDelete(cr_server.muralTable, window, crFree); - if (mural->pVisibleRects) + crServerCheckAllMuralGeometry(NULL); +} + +GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height) +{ + if (mural->width == width && mural->height == height) + return GL_FALSE; + + mural->width = width; + mural->height = height; + + if (cr_server.curClient && cr_server.curClient->currentMural == mural + && !mural->fRedirected) { - crFree(mural->pVisibleRects); + crStateGetCurrent()->buffer.width = mural->width; + crStateGetCurrent()->buffer.height = mural->height; } - crHashtableDelete(cr_server.muralTable, window, crFree); + + crServerCheckAllMuralGeometry(mural); + + return GL_TRUE; } void SERVER_DISPATCH_APIENTRY @@ -256,60 +370,40 @@ crServerDispatchWindowSize( GLint window, GLint width, GLint height ) return; } - mural->width = width; - mural->height = height; + crServerMuralSize(mural, width, height); - if (cr_server.curClient && cr_server.curClient->currentMural == mural) + if (cr_server.currentMural == mural) { - crStateGetCurrent()->buffer.width = mural->width; - crStateGetCurrent()->buffer.height = mural->height; + crServerPerformMakeCurrent( mural, cr_server.currentCtxInfo ); } +} - crServerCheckMuralGeometry(mural); +void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y) +{ + if (mural->gX == x && mural->gY == y) + return; - cr_server.head_spu->dispatch_table.WindowSize(mural->spuWindow, width, height); + mural->gX = x; + mural->gY = y; - /* Work-around Intel driver bug */ - CRASSERT(!cr_server.curClient - || !cr_server.curClient->currentMural - || cr_server.curClient->currentMural == mural); - if (cr_server.curClient && cr_server.curClient->currentMural == mural) - { - CRContextInfo * ctxInfo = cr_server.currentCtxInfo; - CRASSERT(ctxInfo); - crServerDispatchMakeCurrent(mural->spuWindow, 0, ctxInfo->CreateInfo.externalID); - } + crServerCheckAllMuralGeometry(mural); } - void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPosition( GLint window, GLint x, GLint y ) { CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); - /* crDebug("CRServer: Window %d pos %d, %d", window, x, y);*/ if (!mural) { #if EXTRA_WARN crWarning("CRServer: invalid window %d passed to WindowPosition()", window); #endif return; } - mural->gX = x; - mural->gY = y; - - crServerCheckMuralGeometry(mural); + crServerMuralPosition(mural, x, y); } -void SERVER_DISPATCH_APIENTRY -crServerDispatchWindowVisibleRegion( GLint window, GLint cRects, GLint *pRects ) +void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects ) { - CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); - if (!mural) { -#if EXTRA_WARN - crWarning("CRServer: invalid window %d passed to WindowVisibleRegion()", window); -#endif - return; - } - if (mural->pVisibleRects) { crFree(mural->pVisibleRects); @@ -328,17 +422,35 @@ crServerDispatchWindowVisibleRegion( GLint window, GLint cRects, GLint *pRects ) crMemcpy(mural->pVisibleRects, pRects, 4*sizeof(GLint)*cRects); } - cr_server.head_spu->dispatch_table.WindowVisibleRegion(mural->spuWindow, cRects, pRects); + crServerCheckAllMuralGeometry(mural); +} - if (mural->pvOutputRedirectInstance) - { - /* @todo the code assumes that RTRECT == four GLInts. */ - cr_server.outputRedirect.CRORVisibleRegion(mural->pvOutputRedirectInstance, - cRects, (RTRECT *)pRects); +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowVisibleRegion( GLint window, GLint cRects, const GLint *pRects ) +{ + CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to WindowVisibleRegion()", window); +#endif + return; } + + crServerMuralVisibleRegion( mural, cRects, pRects ); } +void crServerMuralShow( CRMuralInfo *mural, GLint state ) +{ + if (!mural->bVisible == !state) + return; + mural->bVisible = !!state; + + if (mural->bVisible) + crServerCheckMuralGeometry(mural); + else + crServerCheckAllMuralGeometry(mural); +} void SERVER_DISPATCH_APIENTRY crServerDispatchWindowShow( GLint window, GLint state ) @@ -351,15 +463,9 @@ crServerDispatchWindowShow( GLint window, GLint state ) return; } - if (!mural->bUseFBO) - { - cr_server.head_spu->dispatch_table.WindowShow(mural->spuWindow, state); - } - - mural->bVisible = state; + crServerMuralShow( mural, state ); } - GLint crServerSPUWindowID(GLint serverWindow) { diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c index 1446473e..b907c6a9 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c @@ -9,9 +9,14 @@ #include "cr_error.h" #include "cr_mem.h" #include "cr_spu.h" +#include "cr_environment.h" #include "renderspu.h" #include "cr_extstring.h" +#include <iprt/asm.h> + +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); static void DoSync(void) @@ -78,6 +83,12 @@ renderspuMakeVisString( GLbitfield visAttribs, char *s ) crStrcat(s, ", PBuffer"); } +GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs) +{ + pVisInfo->displayName = crStrdup(displayName); + pVisInfo->visAttribs = visAttribs; + return renderspu_SystemInitVisual(pVisInfo); +} /* * Find a VisualInfo which matches the given display name and attribute @@ -115,9 +126,7 @@ renderspuFindVisual(const char *displayName, GLbitfield visAttribs) /* create a new visual */ i = render_spu.numVisuals; - render_spu.visuals[i].displayName = crStrdup(displayName); - render_spu.visuals[i].visAttribs = visAttribs; - if (renderspu_SystemInitVisual(&(render_spu.visuals[i]))) { + if (renderspuInitVisual(&(render_spu.visuals[i]), displayName, visAttribs)) { render_spu.numVisuals++; return &(render_spu.visuals[i]); } @@ -127,79 +136,212 @@ renderspuFindVisual(const char *displayName, GLbitfield visAttribs) } } -/* - * Context functions - */ - -GLint RENDER_APIENTRY -renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx) +static ContextInfo * renderspuCreateContextInternal(const char *dpyName, GLint visBits, GLint idCtx, ContextInfo * sharedContext) { - ContextInfo *context, *sharedContext = NULL; + ContextInfo *context; VisualInfo *visual; - if (shareCtx > 0) { - sharedContext - = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx); + if (idCtx <= 0) + { + idCtx = (GLint)crHashtableAllocKeys(render_spu.contextTable, 1); + if (idCtx <= 0) + { + crWarning("failed to allocate context id"); + return NULL; + } } + else + { + if (crHashtableIsKeyUsed(render_spu.contextTable, idCtx)) + { + crWarning("the specified ctx key %d is in use", idCtx); + return NULL; + } + } + if (!dpyName || crStrlen(render_spu.display_string)>0) dpyName = render_spu.display_string; visual = renderspuFindVisual(dpyName, visBits); if (!visual) - return -1; + return NULL; context = (ContextInfo *) crCalloc(sizeof(ContextInfo)); if (!context) - return -1; - context->id = render_spu.context_id; + return NULL; + context->BltInfo.Base.id = idCtx; context->shared = sharedContext; if (!renderspu_SystemCreateContext(visual, context, sharedContext)) - return -1; + return NULL; - crHashtableAdd(render_spu.contextTable, render_spu.context_id, context); - render_spu.context_id++; + crHashtableAdd(render_spu.contextTable, idCtx, context); + context->BltInfo.Base.visualBits = visual->visAttribs; /* crDebug("Render SPU: CreateContext(%s, 0x%x) returning %d", - dpyName, visBits, context->id); + dpyName, visBits, context->BltInfo.Base.id); */ - return context->id; -} + if (sharedContext) + renderspuContextRetain(sharedContext); + context->cRefs = 1; -static void RENDER_APIENTRY -renderspuDestroyContext( GLint ctx ) + return context; +} + +GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx) { - ContextInfo *context; + ContextInfo *context, *sharedContext = NULL; - CRASSERT(ctx); + if (shareCtx) { + sharedContext + = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx); + CRASSERT(sharedContext); + } - context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); - CRASSERT(context); + context = renderspuCreateContextInternal(dpyName, visBits, id, sharedContext); + if (context) + return context->BltInfo.Base.id; + return -1; +} + +/* + * Context functions + */ + +GLint RENDER_APIENTRY +renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx) +{ + return renderspuCreateContextEx(dpyName, visBits, 0, shareCtx); +} + +static void renderspuDestroyContextTerminate( ContextInfo *context ) +{ + CRASSERT(context->BltInfo.Base.id == -1); renderspu_SystemDestroyContext( context ); if (context->extensionString) { crFree(context->extensionString); context->extensionString = NULL; } - crHashtableDelete(render_spu.contextTable, ctx, crFree); + + if (context->shared) + renderspuContextRelease( context->shared ); + + crFree(context); } +uint32_t renderspuContextRetain( ContextInfo *context ) +{ + Assert(context->cRefs); + return ASMAtomicIncU32(&context->cRefs); +} -void RENDER_APIENTRY -renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) +uint32_t renderspuContextRelease( ContextInfo *context ) { - WindowInfo *window; - ContextInfo *context; + uint32_t cRefs = ASMAtomicDecU32(&context->cRefs); + if (!cRefs) + renderspuDestroyContextTerminate( context ); + else + CRASSERT(cRefs < UINT32_MAX/2); + return cRefs; +} - /* - crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx); - */ +uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ) +{ + /* invalidate the context id to mark it as deleted */ + context->BltInfo.Base.id = -1; + + /* some drivers do not like when the base (shared) context is deleted before its referals, + * this is why we keep a context refference counting the base (shared) context will be destroyed as soon as*/ + return renderspuContextRelease( context ); +} + +ContextInfo * renderspuDefaultSharedContextAcquire() +{ + ContextInfo * pCtx = render_spu.defaultSharedContext; + if (!pCtx) + return NULL; + + renderspuContextRetain(pCtx); + return pCtx; +} + +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx) +{ + renderspuContextRelease(pCtx); +} + + +static void RENDER_APIENTRY +renderspuDestroyContext( GLint ctx ) +{ + ContextInfo *context, *curCtx; + + CRASSERT(ctx); + + if (ctx == CR_RENDER_DEFAULT_CONTEXT_ID) + { + crWarning("request to destroy a default context, ignoring"); + return; + } - window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow); context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + if (!context) + { + crWarning("request to delete inexistent context"); + return; + } + + if (render_spu.defaultSharedContext == context) + { + renderspuSetDefaultSharedContext(NULL); + } + + curCtx = GET_CONTEXT_VAL(); +// CRASSERT(curCtx); + if (curCtx == context) + { + renderspuMakeCurrent( CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID ); + curCtx = GET_CONTEXT_VAL(); + Assert(curCtx); + Assert(curCtx != context); + } + + crHashtableDelete(render_spu.contextTable, ctx, NULL); + + renderspuContextMarkDeletedAndRelease(context); +} + +WindowInfo* renderspuGetDummyWindow(GLint visBits) +{ + WindowInfo *window = (WindowInfo *) crHashtableSearch(render_spu.dummyWindowTable, visBits); + if (!window) + { + window = (WindowInfo *)crAlloc(sizeof (*window)); + if (!window) + { + crWarning("crAlloc failed"); + return NULL; + } + + if (!renderspuWindowInit(window, NULL, visBits, -1)) + { + crWarning("renderspuWindowInit failed"); + crFree(window); + return NULL; + } + + crHashtableAdd(render_spu.dummyWindowTable, visBits, window); + } + + return window; +} + +void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context) +{ if (window && context) { #ifdef CHROMIUM_THREADSAFE @@ -210,12 +352,12 @@ renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) context->currentWindow = window; if (!window) { - crDebug("Render SPU: MakeCurrent invalid window id: %d", crWindow); + crDebug("Render SPU: MakeCurrent invalid window id: %d", window->BltInfo.Base.id); return; } if (!context) { - crDebug("Render SPU: MakeCurrent invalid context id: %d", ctx); + crDebug("Render SPU: MakeCurrent invalid context id: %d", context->BltInfo.Base.id); return; } @@ -236,9 +378,9 @@ renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) context->haveWindowPosARB = GL_FALSE; context->everCurrent = GL_TRUE; } - if (crWindow == 0 && window->mapPending && + if (window->BltInfo.Base.id == CR_RENDER_DEFAULT_WINDOW_ID && window->mapPending && !render_spu.render_to_app_window && !render_spu.render_to_crut_window) { - /* Window[0] is special, it's the default window and normally hidden. + /* Window[CR_RENDER_DEFAULT_CONTEXT_ID] is special, it's the default window and normally hidden. * If the mapPending flag is set, then we should now make the window * visible. */ @@ -247,60 +389,75 @@ renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) } window->everCurrent = GL_TRUE; } - else + else if (!window && !context) { + renderspu_SystemMakeCurrent( NULL, 0, NULL ); #ifdef CHROMIUM_THREADSAFE crSetTSD(&_RenderTSD, NULL); #else render_spu.currentContext = NULL; #endif } + else + { + crError("renderspuMakeCurrent invalid ids: crWindow(%d), ctx(%d)", + window ? window->BltInfo.Base.id : 0, + context ? context->BltInfo.Base.id : 0); + } } - -/* - * Window functions - */ - -GLint RENDER_APIENTRY -renderspuWindowCreate( const char *dpyName, GLint visBits ) +void RENDER_APIENTRY +renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) { - WindowInfo *window; - VisualInfo *visual; - GLboolean showIt; + WindowInfo *window = NULL; + ContextInfo *context = NULL; - if (!dpyName || crStrlen(render_spu.display_string) > 0) - dpyName = render_spu.display_string; + /* + crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx); + */ - visual = renderspuFindVisual( dpyName, visBits ); - if (!visual) + if (crWindow) { - crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" ); - return -1; + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow); + if (!window) + { + crWarning("invalid window %d specified", crWindow); + return; + } } - /* Allocate WindowInfo */ - window = (WindowInfo *) crCalloc(sizeof(WindowInfo)); - if (!window) + if (ctx) { - crWarning( "Render SPU: Couldn't create a window" ); - return -1; + context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + if (!context) + { + crWarning("invalid context %d specified", ctx); + return; + } } - crHashtableAdd(render_spu.windowTable, render_spu.window_id, window); - window->id = render_spu.window_id; - render_spu.window_id++; + if (!context != !window) + { + crWarning("either window %d or context %d are zero", crWindow, ctx); + return; + } + + renderspuPerformMakeCurrent(window, nativeWindow, context); +} + +GLboolean renderspuWindowInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ) +{ + crMemset(window, 0, sizeof (*window)); + RTCritSectInit(&window->CompositorLock); + window->fCompositorPresentEmpty = GL_FALSE; + window->pCompositor = NULL; + + window->BltInfo.Base.id = id; window->x = render_spu.defaultX; window->y = render_spu.defaultY; - window->width = render_spu.defaultWidth; - window->height = render_spu.defaultHeight; - - if (render_spu.force_hidden_wdn_create - || ((render_spu.render_to_app_window || render_spu.render_to_crut_window) && !crGetenv("CRNEWSERVER"))) - showIt = 0; - else - showIt = window->id > 0; + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; /* Set window->title, replacing %i with the window ID number */ { @@ -310,7 +467,7 @@ renderspuWindowCreate( const char *dpyName, GLint visBits ) window->title = crAlloc(crStrlen(render_spu.window_title) + 10); for (i = 0; render_spu.window_title[i] != '%'; i++) window->title[i] = render_spu.window_title[i]; - k = sprintf(window->title + i, "%d", window->id); + k = sprintf(window->title + i, "%d", window->BltInfo.Base.id); CRASSERT(k < 10); i++; /* skip the 'i' after the '%' */ j = i + k; @@ -321,21 +478,103 @@ renderspuWindowCreate( const char *dpyName, GLint visBits ) window->title = crStrdup(render_spu.window_title); } } - + + window->BltInfo.Base.visualBits = visual->visAttribs; + + /* - crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->id); + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); */ /* Have GLX/WGL/AGL create the window */ if (!renderspu_SystemVBoxCreateWindow( visual, showIt, window )) { - crFree(window); crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); - return -1; + return GL_FALSE; } + + window->visible = !!showIt; CRASSERT(window->visual == visual); + return GL_TRUE; +} + +/* + * Window functions + */ +GLboolean renderspuWindowInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id) +{ + VisualInfo *visual; + + crMemset(pWindow, 0, sizeof (*pWindow)); - return window->id; + if (!dpyName || crStrlen(render_spu.display_string) > 0) + dpyName = render_spu.display_string; + + visual = renderspuFindVisual( dpyName, visBits ); + if (!visual) + { + crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" ); + return GL_FALSE; + } + + /* + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); + */ + /* Have GLX/WGL/AGL create the window */ + if (!renderspuWindowInitWithVisual( pWindow, visual, 0, id )) + { + crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ) +{ + WindowInfo *window; + + if (id <= 0) + { + id = (GLint)crHashtableAllocKeys(render_spu.windowTable, 1); + if (id <= 0) + { + crWarning("failed to allocate window id"); + return -1; + } + } + else + { + if (crHashtableIsKeyUsed(render_spu.windowTable, id)) + { + crWarning("the specified window key %d is in use", id); + return -1; + } + } + + /* Allocate WindowInfo */ + window = (WindowInfo *) crCalloc(sizeof(WindowInfo)); + if (!window) + { + crWarning( "Render SPU: Couldn't create a window" ); + return -1; + } + + if (!renderspuWindowInit(window, dpyName, visBits, id)) + { + crWarning("renderspuWindowInit failed"); + crFree(window); + return -1; + } + + crHashtableAdd(render_spu.windowTable, id, window); + return window->BltInfo.Base.id; +} + +GLint RENDER_APIENTRY +renderspuWindowCreate( const char *dpyName, GLint visBits ) +{ + return renderspuWindowCreateEx( dpyName, visBits, 0 ); } static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, void *data2) @@ -346,7 +585,46 @@ static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, voi if (pCtx->currentWindow==pWindow) { - renderspuMakeCurrent(0, 0, pCtx->id); + WindowInfo* pDummy = renderspuGetDummyWindow(pCtx->BltInfo.Base.visualBits); + if (pDummy) + { + renderspuPerformMakeCurrent(pDummy, 0, pCtx); + } + else + { + crWarning("failed to get dummy window"); + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, pCtx->BltInfo.Base.id); + } + } +} + +void renderspuWindowTerm( WindowInfo *window ) +{ + GET_CONTEXT(pOldCtx); + WindowInfo * pOldWindow = pOldCtx ? pOldCtx->currentWindow : NULL; + CRASSERT(!pOldCtx == !pOldWindow); + /* ensure no concurrent draws can take place */ + renderspuVBoxCompositorSet(window, NULL); + renderspuVBoxPresentBlitterCleanup(window); + renderspu_SystemDestroyWindow( window ); + RTCritSectDelete(&window->CompositorLock); + /* check if this window is bound to some ctx. Note: window pointer is already freed here */ + crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window); + /* restore current context */ + { + GET_CONTEXT(pNewCtx); + WindowInfo * pNewWindow = pNewCtx ? pNewCtx->currentWindow : NULL; + CRASSERT(!pNewCtx == !pNewWindow); + + if (pOldWindow == window) + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + else if (pNewCtx != pOldCtx || pOldWindow != pNewWindow) + { + if (pOldCtx) + renderspuPerformMakeCurrent(pOldWindow, 0, pOldCtx); + else + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + } } } @@ -354,28 +632,21 @@ void RENDER_APIENTRY renderspuWindowDestroy( GLint win ) { WindowInfo *window; - GET_CONTEXT(pOldCtx); CRASSERT(win >= 0); + if (win == CR_RENDER_DEFAULT_WINDOW_ID) + { + crWarning("request to destroy a default mural, ignoring"); + return; + } window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); if (window) { crDebug("Render SPU: Destroy window (%d)", win); - renderspu_SystemDestroyWindow( window ); + renderspuWindowTerm( window ); + /* remove window info from hash table, and free it */ crHashtableDelete(render_spu.windowTable, win, crFree); - /* check if this window is bound to some ctx. Note: window pointer is already freed here */ - crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window); - - /* restore current context */ - { - GET_CONTEXT(pNewCtx); - if (pNewCtx!=pOldCtx) - { - renderspuMakeCurrent(pOldCtx&&pOldCtx->currentWindow ? pOldCtx->currentWindow->id:0, 0, - pOldCtx ? pOldCtx->id:0); - } - } } else { crDebug("Render SPU: Attempt to destroy invalid window (%d)", win); @@ -390,7 +661,17 @@ renderspuWindowSize( GLint win, GLint w, GLint h ) CRASSERT(win >= 0); window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); if (window) { - renderspu_SystemWindowSize( window, w, h ); + if (w != window->BltInfo.width + || h != window->BltInfo.height) + { + /* window is resized, compositor data is no longer valid + * this set also ensures all redraw operations are done in the redraw thread + * and that no redraw is started until new Present request comes containing a valid presentation data */ + renderspuVBoxCompositorSet( window, NULL); + renderspu_SystemWindowSize( window, w, h ); + window->BltInfo.width = w; + window->BltInfo.height = h; + } } else { crDebug("Render SPU: Attempt to resize invalid window (%d)", win); @@ -416,17 +697,41 @@ renderspuWindowPosition( GLint win, GLint x, GLint y ) } } +#ifdef DEBUG_misha +# define CR_DBG_DUMP_VISIBLE_REGIONS +#endif + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS +static void renderspuDbgDumpVisibleRegion(GLint win, GLint cRects, const GLint *pRects) +{ + GLint i; + const RTRECT *pRtRects = (const RTRECT *)((const void*)pRects); + + crInfo("Window %d, Vidible Regions%d", win, cRects); + for (i = 0; i < cRects; ++i) + { + crInfo("%d: (%d,%d), (%d,%d)", i, pRtRects[i].xLeft, pRtRects[i].yTop, pRtRects[i].xRight, pRtRects[i].yBottom); + } + crInfo("======"); +} +#endif + static void RENDER_APIENTRY -renderspuWindowVisibleRegion(GLint win, GLint cRects, GLint *pRects) +renderspuWindowVisibleRegion(GLint win, GLint cRects, const GLint *pRects) { WindowInfo *window; CRASSERT(win >= 0); + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS + renderspuDbgDumpVisibleRegion(win, cRects, pRects); +#endif + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); if (window) { renderspu_SystemWindowVisibleRegion( window, cRects, pRects ); } else { - crDebug("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win); + crWarning("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win); } } @@ -437,6 +742,7 @@ renderspuWindowShow( GLint win, GLint flag ) CRASSERT(win >= 0); window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); if (window) { + GLboolean visible; if (window->nativeWindow) { /* We're rendering back to the native app window instead of the * new window which we (the Render SPU) created earlier. @@ -444,13 +750,367 @@ renderspuWindowShow( GLint win, GLint flag ) */ flag = 0; } - renderspu_SystemShowWindow( window, (GLboolean) flag ); + + visible = !!flag; + + if (window->visible != visible) + { + renderspu_SystemShowWindow( window, visible ); + window->visible = visible; + } } else { crDebug("Render SPU: Attempt to hide/show invalid window (%d)", win); } } +static void RENDER_APIENTRY +renderspuVBoxPresentComposition( GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + if (pCompositor && CrVrScrCompositorIsEmpty(pCompositor) && !window->fCompositorPresentEmpty) + pCompositor = NULL; + + if (pCompositor) + window->fCompositorPresentEmpty = GL_FALSE; + + renderspuVBoxCompositorSet( window, pCompositor); + if (pCompositor) + { + renderspu_SystemVBoxPresentComposition(window, pChangedEntry); + } + } + else { + crDebug("Render SPU: Attempt to PresentComposition for invalid window (%d)", win); + } +} + +void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + RTRECT DstRect; + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + DstRect.xLeft = paDstRegions[i].xLeft * scaleX; + DstRect.yTop = paDstRegions[i].yTop * scaleY; + DstRect.xRight = paDstRegions[i].xRight * scaleX; + DstRect.yBottom = paDstRegions[i].yBottom * scaleY; + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), &paSrcRegions[i], &DstRect, 1, fFlags); + } + } + else + { + crWarning("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), paSrcRegions, paDstRegions, cRegions, fFlags); + } + else + { + crWarning("Blit: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ) +{ + if (!window->pBlitter) + return; + + if (render_spu.blitterTable) + { + const CR_BLITTER_WINDOW * pBltInfo = CrBltMuralGetCurrentInfo(window->pBlitter); + if (pBltInfo && pBltInfo->Base.id == window->BltInfo.Base.id) + { + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + } + } + else + { + CRASSERT(CrBltMuralGetCurrentInfo(window->pBlitter)->Base.id == window->BltInfo.Base.id); + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + CrBltTerm(window->pBlitter); + } + window->pBlitter = NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ) +{ + PCR_BLITTER pBlitter = window->pBlitter; + if (!pBlitter) + { + if (render_spu.blitterTable) + { + crHashtableLock(render_spu.blitterTable); + pBlitter = (PCR_BLITTER)crHashtableSearch(render_spu.blitterTable, window->visual->visAttribs); + } + + if (!pBlitter) + { + int rc; + ContextInfo * pDefaultCtxInfo; + + pBlitter = (PCR_BLITTER)crCalloc(sizeof (*pBlitter)); + if (!pBlitter) + { + crWarning("failed to allocate blitter"); + return NULL; + } + + pDefaultCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pDefaultCtxInfo) + { + crWarning("no default ctx info!"); + crFree(pBlitter); + return NULL; + } + + rc = CrBltInit(pBlitter, &pDefaultCtxInfo->BltInfo, true, true, NULL, &render_spu.blitterDispatch); + + /* we can release it either way, since it will be retained when used as a shared context */ + renderspuDefaultSharedContextRelease(pDefaultCtxInfo); + + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltInit failed, rc %d", rc); + crFree(pBlitter); + return NULL; + } + + if (render_spu.blitterTable) + { + crHashtableAdd( render_spu.blitterTable, window->visual->visAttribs, pBlitter ); + } + } + + if (render_spu.blitterTable) + crHashtableUnlock(render_spu.blitterTable); + + Assert(pBlitter); + window->pBlitter = pBlitter; + } + + CrBltMuralSetCurrentInfo(pBlitter, &window->BltInfo); + return pBlitter; +} + +int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData) +{ + int rc; + + CrBltSetMakeCurrentUserData(pBlitter, i32MakeCurrentUserData); + + rc = CrBltEnter(pBlitter); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltEnter failed, rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData ) +{ + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet(window); + if (pBlitter) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + return pBlitter; + } + } + return NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ) +{ + if (!window->pBlitter) + { + const struct VBOXVR_SCR_COMPOSITOR * pTmpCompositor; + /* just use compositor lock to synchronize */ + pTmpCompositor = renderspuVBoxCompositorAcquire(window); + CRASSERT(pTmpCompositor); + if (pTmpCompositor) + { + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet( window ); + if (pBlitter) + { + if (!CrBltIsEverEntered(pBlitter)) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + CrBltLeave(pBlitter); + } + else + { + crWarning("renderspuVBoxPresentBlitterEnter failed rc %d", rc); + } + } + } + else + { + crWarning("renderspuVBoxPresentBlitterGet failed"); + } + + renderspuVBoxCompositorRelease(window); + } + else + { + crWarning("renderspuVBoxCompositorAcquire failed"); + } + } + return window->pBlitter; +} + +void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData ) +{ + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGetAndEnter(window, i32MakeCurrentUserData); + if (!pBlitter) + return; + + renderspuVBoxCompositorBlit(pCompositor, pBlitter); + + renderspu_SystemSwapBuffers(window, 0); + + CrBltLeave(pBlitter); +} + +void renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor) +{ + int rc; + /* renderspuVBoxCompositorSet can be invoked from the chromium thread only and is not reentrant, + * no need to synch here + * the lock is actually needed to ensure we're in synch with the redraw thread */ + if (window->pCompositor == pCompositor) + return; + rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + window->pCompositor = pCompositor; + RTCritSectLeave(&window->CompositorLock); + return; + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + } +} + +static void renderspuVBoxCompositorClearAllCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + renderspuVBoxCompositorSet(window, NULL); +} + +void renderspuVBoxCompositorClearAll() +{ + /* we need to clear window compositor, which is not that trivial though, + * since the lock order used in presentation thread is compositor lock() -> hash table lock (aquired for id->window resolution) + * this is why, to prevent potential deadlocks, we use crHashtableWalkUnlocked that does not hold the table lock + * we are can be sure noone will modify the table here since renderspuVBoxCompositorClearAll can be called in the command (hgcm) thread only, + * and the table can be modified from that thread only as well */ + crHashtableWalkUnlocked(render_spu.windowTable, renderspuVBoxCompositorClearAllCB, NULL); +} + +const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + const VBOXVR_SCR_COMPOSITOR * pCompositor = window->pCompositor; + if (pCompositor) + return pCompositor; + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + } + return NULL; +} + +int renderspuVBoxCompositorLock(WindowInfo *window) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + AssertRC(rc); + return rc; +} + +int renderspuVBoxCompositorUnlock(WindowInfo *window) +{ + int rc = RTCritSectLeave(&window->CompositorLock); + AssertRC(rc); + return rc; +} + +int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor) +{ + int rc = RTCritSectTryEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + *ppCompositor = window->pCompositor; + if (*ppCompositor) + return VINF_SUCCESS; + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + rc = VERR_INVALID_STATE; + } + else + { + *ppCompositor = NULL; + } + return rc; +} + +void renderspuVBoxCompositorRelease( WindowInfo *window) +{ + int rc; + Assert(window->pCompositor); + if (CrVrScrCompositorIsEmpty(window->pCompositor) && RTCritSectGetRecursion(&window->CompositorLock) == 1) + window->pCompositor = NULL; + rc = RTCritSectLeave(&window->CompositorLock); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectLeave failed rc %d", rc); + } +} + /* * Set the current raster position to the given window coordinate. @@ -691,7 +1351,20 @@ static void RENDER_APIENTRY renderspuSemaphoreVCR( GLuint name ) /* * Misc functions */ +void renderspuSetDefaultSharedContext(ContextInfo *pCtx) +{ + if (pCtx == render_spu.defaultSharedContext) + return; + renderspu_SystemDefaultSharedContextChanged(render_spu.defaultSharedContext, pCtx); + + if (render_spu.defaultSharedContext) + renderspuContextRelease(render_spu.defaultSharedContext); + + if (pCtx) + renderspuContextRetain(pCtx); + render_spu.defaultSharedContext = pCtx; +} static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint value) @@ -699,9 +1372,17 @@ static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint v switch (target) { - case GL_HOST_WND_CREATED_HIDDEN: - render_spu.force_hidden_wdn_create = value ? GL_TRUE : GL_FALSE; + case GL_HH_SET_DEFAULT_SHARED_CTX: + { + ContextInfo * pCtx = NULL; + if (value) + pCtx = (ContextInfo *)crHashtableSearch(render_spu.contextTable, value); + else + crWarning("invalid default shared context id %d", value); + + renderspuSetDefaultSharedContext(pCtx); break; + } default: // crWarning("Unhandled target in renderspuChromiumParameteriCR()"); break; @@ -841,7 +1522,7 @@ renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count, case GL_WINDOW_SIZE_CR: /* XXX this is old code that should be removed. - * NOTE: we can only resize the default (id=0) window!!! + * NOTE: we can only resize the default (id=CR_RENDER_DEFAULT_WINDOW_ID) window!!! */ { GLint w, h; @@ -851,7 +1532,7 @@ renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count, CRASSERT(values); w = ((GLint*)values)[0]; h = ((GLint*)values)[1]; - window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); if (window) { renderspu_SystemWindowSize(window, w, h); @@ -859,9 +1540,16 @@ renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count, } break; + case GL_HH_SET_TMPCTX_MAKE_CURRENT: + if (type == GL_BYTE && count == sizeof (void*)) + memcpy(&render_spu.blitterDispatch.MakeCurrent, values, count); + else + WARN(("unexpected type(%#x) - count(%d) pair", type, count)); + break; + default: #if 0 - crWarning("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target); + WARN(("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target)); #endif break; } @@ -923,6 +1611,21 @@ renderspuGetChromiumParametervCR(GLenum target, GLuint index, GLenum type, } } break; + case GL_WINDOW_VISIBILITY_CR: + { + GLint *vis = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 1); + CRASSERT(values); + vis[0] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + vis[0] = window->visible; + } + } + break; default: ; /* nothing - silence compiler */ } @@ -1001,6 +1704,9 @@ renderspuGetString(GLenum pname) return NULL; } + if (!context) + return (const GLubyte *)nativeExt; + crExt = crStrjoin3(crExtensions, " ", crAppOnlyExtensions); s1 = crStrIntersect(nativeExt, crExt); remove_trailing_space(s1); @@ -1043,6 +1749,13 @@ renderspuGetString(GLenum pname) return NULL; } +static void renderspuReparentWindowCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *pWindow = (WindowInfo *)data1; + + renderspu_SystemReparentWindow(pWindow); +} + DECLEXPORT(void) renderspuReparentWindow(GLint window) { WindowInfo *pWindow; @@ -1057,46 +1770,13 @@ DECLEXPORT(void) renderspuReparentWindow(GLint window) } renderspu_SystemReparentWindow(pWindow); -} - -#if defined(DARWIN) -# ifdef VBOX_WITH_COCOA_QT -void renderspuFlush() -{ - renderspu_SystemFlush(); -} - -void renderspuFinish() -{ - renderspu_SystemFinish(); -} - -void renderspuBindFramebufferEXT(GLenum target, GLuint framebuffer) -{ - renderspu_SystemBindFramebufferEXT(target, framebuffer); -} -void renderspuCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) -{ - renderspu_SystemCopyPixels(x, y, width, height, type); -} - -void renderspuGetIntegerv(GLenum pname, GLint * params) -{ - renderspu_SystemGetIntegerv(pname, params); -} - -void renderspuDrawBuffer(GLenum mode) -{ - renderspu_SystemDrawBuffer(mode); -} - -void renderspuReadBuffer(GLenum mode) -{ - renderspu_SystemReadBuffer(mode); + /* special case: reparent all internal windows as well */ + if (window == CR_RENDER_DEFAULT_WINDOW_ID) + { + crHashtableWalk(render_spu.dummyWindowTable, renderspuReparentWindowCB, NULL); + } } -# endif -#endif #define FILLIN( NAME, FUNC ) \ table[i].name = crStrdup(NAME); \ @@ -1134,16 +1814,6 @@ renderspuCreateFunctions(SPUNamedFunctionTable table[]) FILLIN( "ChromiumParametervCR", renderspuChromiumParametervCR ); FILLIN( "GetChromiumParametervCR", renderspuGetChromiumParametervCR ); FILLIN( "GetString", renderspuGetString ); -#if defined(DARWIN) -# ifdef VBOX_WITH_COCOA_QT - FILLIN( "Flush", renderspuFlush ); - FILLIN( "Finish", renderspuFinish ); - FILLIN( "BindFramebufferEXT", renderspuBindFramebufferEXT ); - FILLIN( "CopyPixels", renderspuCopyPixels ); - FILLIN( "GetIntegerv", renderspuGetIntegerv ); - FILLIN( "ReadBuffer", renderspuReadBuffer ); - FILLIN( "DrawBuffer", renderspuDrawBuffer ); -# endif -#endif + FILLIN( "VBoxPresentComposition", renderspuVBoxPresentComposition ); return i; } diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h index d39af70d..1d7704c1 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu.h +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h @@ -27,8 +27,22 @@ #include "cr_spu.h" #include "cr_hash.h" #include "cr_server.h" +#include "cr_blitter.h" +#include "cr_compositor.h" #include <iprt/cdefs.h> +#include <iprt/critsect.h> +#if defined(GLX) /* @todo: unify windows and glx thread creation code */ +#include <iprt/thread.h> +#include <iprt/semaphore.h> + +/* special window id used for representing the command window CRWindowInfo */ +#define CR_RENDER_WINCMD_ID (INT32_MAX-2) +AssertCompile(CR_RENDER_WINCMD_ID != CR_RENDER_DEFAULT_WINDOW_ID); +/* CRHashTable is using unsigned long keys, we use it to trore X Window -> CRWindowInfo association */ +AssertCompile(sizeof (Window) == sizeof (unsigned long)); +#endif + #define MAX_VISUALS 32 @@ -76,19 +90,32 @@ typedef struct { /** * Window info */ -typedef struct { +typedef struct WindowInfo { int x, y; - int width, height; - int id; /**< integer window ID */ +// int width, height; +// int id; /**< integer window ID */ + CR_BLITTER_WINDOW BltInfo; + VisualInfo *visual; GLboolean mapPending; GLboolean visible; GLboolean everCurrent; /**< has this window ever been bound? */ + GLboolean fCompositorPresentEmpty; char *title; + + const VBOXVR_SCR_COMPOSITOR *pCompositor; + /* the composotor lock is used to synchronize the current compositor access, + * i.e. the compositor can be accessed by a gui refraw thread, + * while chromium thread might try to set a new compositor + * note that the compositor internally has its own lock to be used for accessing its data + * see CrVrScrCompositorLock/Unlock; renderspu and crserverlib would use it for compositor data access */ + RTCRITSECT CompositorLock; + PCR_BLITTER pBlitter; #if defined(WINDOWS) HDC nativeWindow; /**< for render_to_app_window */ HWND hWnd; HDC device_context; + HDC redraw_device_context; HRGN hRgn; #elif defined(DARWIN) # ifndef VBOX_WITH_COCOA_QT @@ -124,7 +151,8 @@ typedef struct { * Context Info */ typedef struct _ContextInfo { - int id; /**< integer context ID */ +// int id; /**< integer context ID */ + CR_BLITTER_CONTEXT BltInfo; VisualInfo *visual; GLboolean everCurrent; GLboolean haveWindowPosARB; @@ -142,6 +170,7 @@ typedef struct _ContextInfo { #endif struct _ContextInfo *shared; char *extensionString; + volatile uint32_t cRefs; } ContextInfo; /** @@ -152,6 +181,44 @@ typedef struct { GLuint count; } Barrier; +#ifdef GLX +typedef enum +{ + CR_RENDER_WINCMD_TYPE_UNDEFINED = 0, + /* create the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_CREATE, + /* destroy the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_DESTROY, + /* notify the WinCmd thread about window creation */ + CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, + /* notify the WinCmd thread about window destroy */ + CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, + /* nop used to synchronize with the WinCmd thread */ + CR_RENDER_WINCMD_TYPE_NOP, + /* exit Win Cmd thread */ + CR_RENDER_WINCMD_TYPE_EXIT, +} CR_RENDER_WINCMD_TYPE; + +typedef struct CR_RENDER_WINCMD +{ + /* command type */ + CR_RENDER_WINCMD_TYPE enmCmd; + /* command result */ + int rc; + /* valid for WIN_CREATE & WIN_DESTROY only */ + WindowInfo *pWindow; +} CR_RENDER_WINCMD, *PCR_RENDER_WINCMD; +#endif + +#ifdef RT_OS_DARWIN +typedef void (*PFNDELETE_OBJECT)(GLhandleARB obj); +typedef void (*PFNGET_ATTACHED_OBJECTS)( GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj ); +typedef GLhandleARB (*PFNGET_HANDLE)(GLenum pname); +typedef void (*PFNGET_INFO_LOG)( GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ); +typedef void (*PFNGET_OBJECT_PARAMETERFV)( GLhandleARB obj, GLenum pname, GLfloat * params ); +typedef void (*PFNGET_OBJECT_PARAMETERIV)( GLhandleARB obj, GLenum pname, GLint * params ); +#endif + /** * Renderspu state info */ @@ -159,9 +226,6 @@ typedef struct { SPUDispatchTable self; int id; - unsigned int window_id; - unsigned int context_id; - /** config options */ /*@{*/ char *window_title; @@ -204,6 +268,10 @@ typedef struct { CRHashTable *windowTable; CRHashTable *contextTable; + CRHashTable *dummyWindowTable; + + ContextInfo *defaultSharedContext; + #ifndef CHROMIUM_THREADSAFE ContextInfo *currentContext; #endif @@ -217,6 +285,9 @@ typedef struct { char *swap_master_url; CRConnection **swap_conns; + SPUDispatchTable blitterDispatch; + CRHashTable *blitterTable; + #ifdef USE_OSMESA /** Off screen rendering hooks. */ int use_osmesa; @@ -230,13 +301,34 @@ typedef struct { void (*OSMesaDestroyContext)( OSMesaContext ctx ); #endif +#if defined(GLX) + RTTHREAD hWinCmdThread; + VisualInfo WinCmdVisual; + WindowInfo WinCmdWindow; + RTSEMEVENT hWinCmdCompleteEvent; + /* display connection used to send data to the WinCmd thread */ + Display *pCommunicationDisplay; + Atom WinCmdAtom; + /* X Window -> CRWindowInfo table */ + CRHashTable *pWinToInfoTable; +#endif + #ifdef RT_OS_WINDOWS DWORD dwWinThreadId; HANDLE hWinThreadReadyEvent; #endif #ifdef RT_OS_DARWIN -# ifndef VBOX_WITH_COCOA_QT +# ifdef VBOX_WITH_COCOA_QT + PFNDELETE_OBJECT pfnDeleteObject; + PFNGET_ATTACHED_OBJECTS pfnGetAttachedObjects; + PFNGET_HANDLE pfnGetHandle; + PFNGET_INFO_LOG pfnGetInfoLog; + PFNGET_OBJECT_PARAMETERFV pfnGetObjectParameterfv; + PFNGET_OBJECT_PARAMETERIV pfnGetObjectParameteriv; + + CR_GLSL_CACHE GlobalShaders; +# else RgnHandle hRootVisibleRegion; RTSEMFASTMUTEX syncMutex; EventHandlerUPP hParentEventHandler; @@ -247,8 +339,6 @@ typedef struct { bool fInit; # endif #endif /* RT_OS_DARWIN */ - - int force_hidden_wdn_create; } RenderSPU; #ifdef RT_OS_WINDOWS @@ -279,11 +369,22 @@ extern uint64_t render_spu_parent_window_id; #ifdef CHROMIUM_THREADSAFE extern CRtsd _RenderTSD; -#define GET_CONTEXT(T) ContextInfo *T = (ContextInfo *) crGetTSD(&_RenderTSD) +#define GET_CONTEXT_VAL() ((ContextInfo *) crGetTSD(&_RenderTSD)) +#define SET_CONTEXT_VAL(_v) do { \ + crSetTSD(&_RenderTSD, (_v)); \ + } while (0) #else -#define GET_CONTEXT(T) ContextInfo *T = render_spu.currentContext +#define GET_CONTEXT_VAL() (render_spu.currentContext) +#define SET_CONTEXT_VAL(_v) do { \ + render_spu.currentContext = (_v); \ + } while (0) + #endif +#define GET_CONTEXT(T) ContextInfo *T = GET_CONTEXT_VAL() + + +extern void renderspuSetDefaultSharedContext(ContextInfo *pCtx); extern void renderspuSetVBoxConfiguration( RenderSPU *spu ); extern void renderspuMakeVisString( GLbitfield visAttribs, char *s ); extern VisualInfo *renderspuFindVisual(const char *displayName, GLbitfield visAttribs ); @@ -297,26 +398,41 @@ extern void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ); extern void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h ); extern void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ); extern void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ); -extern void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, GLint* pRects); -extern void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window); -#ifdef RT_OS_DARWIN -extern void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects); -# ifdef VBOX_WITH_COCOA_QT -extern void renderspu_SystemFlush(); -extern void renderspu_SystemFinish(); -extern void renderspu_SystemBindFramebufferEXT(GLenum target, GLuint framebuffer); -extern void renderspu_SystemCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); -extern void renderspu_SystemGetIntegerv(GLenum pname, GLint *params); -extern void renderspu_SystemReadBuffer(GLenum mode); -extern void renderspu_SystemDrawBuffer(GLenum mode); -# endif -#endif +extern void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects); +extern int renderspu_SystemInit(); +extern int renderspu_SystemTerm(); +extern void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext); extern void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ); extern void renderspu_SystemMakeCurrent( WindowInfo *window, GLint windowInfor, ContextInfo *context ); extern void renderspu_SystemSwapBuffers( WindowInfo *window, GLint flags ); extern void renderspu_SystemReparentWindow(WindowInfo *window); +extern void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ); +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable); extern void renderspu_GCWindow(void); extern int renderspuCreateFunctions( SPUNamedFunctionTable table[] ); +extern void renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor); +extern void renderspuVBoxCompositorClearAll(); +extern int renderspuVBoxCompositorLock(WindowInfo *window); +extern int renderspuVBoxCompositorUnlock(WindowInfo *window); +extern const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window); +extern int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor); +extern void renderspuVBoxCompositorRelease( WindowInfo *window); +extern void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData ); +extern PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ); +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ); +extern int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData ); +extern PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData ); +extern PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ); +extern void renderspuWindowTerm( WindowInfo *window ); +extern WindowInfo* renderspuGetDummyWindow(GLint visBits); +extern void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context); +extern GLboolean renderspuWindowInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id); +extern GLboolean renderspuWindowInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ); +extern GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs); +extern void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter); +extern void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY); +extern GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx); +extern GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ); extern GLint RENDER_APIENTRY renderspuWindowCreate( const char *dpyName, GLint visBits ); void RENDER_APIENTRY renderspuWindowDestroy( GLint win ); @@ -324,11 +440,17 @@ extern GLint RENDER_APIENTRY renderspuCreateContext( const char *dpyname, GLint extern void RENDER_APIENTRY renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx); extern void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags ); +extern uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ); + +ContextInfo * renderspuDefaultSharedContextAcquire(); +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx); +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); + #ifdef __cplusplus extern "C" { #endif DECLEXPORT(void) renderspuSetWindowId(uint64_t winId); -DECLEXPORT(void) renderspuSetRootVisibleRegion(GLint cRects, GLint *pRects); DECLEXPORT(void) renderspuReparentWindow(GLint window); #ifdef __cplusplus } diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c index 822e56b9..3d14fa3b 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c @@ -65,6 +65,9 @@ DEBUG_MSG_RESULT(result, text); \ } +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window); +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects); + /* In some case (like compiz which doesn't provide us with clipping regions) we * have to make sure that *all* open OpenGL windows are clipped to the main * application window. This is done here when called from the event handler @@ -340,7 +343,7 @@ renderspuWindowAttachContext(WindowInfo *wi, WindowRef window, if(!context || !wi) return render_spu.ws.aglSetCurrentContext( NULL ); -/* DEBUG_MSG_POETZSCH (("WindowAttachContext %d\n", wi->id));*/ +/* DEBUG_MSG_POETZSCH (("WindowAttachContext %d\n", wi->BltInfo.Base.id));*/ /* Flush old context first */ if (context->currentWindow->window != window) @@ -349,7 +352,7 @@ renderspuWindowAttachContext(WindowInfo *wi, WindowRef window, * dummy context. */ if (wi->bufferName == -1) { - DEBUG_MSG_POETZSCH (("WindowAttachContext: create context %d\n", wi->id)); + DEBUG_MSG_POETZSCH (("WindowAttachContext: create context %d\n", wi->BltInfo.Base.id)); /* Use the same visual bits as those in the context structure */ AGLPixelFormat pix; if( !renderspuChoosePixelFormat(context, &pix) ) @@ -519,10 +522,10 @@ renderspu_SystemWindowSize(WindowInfo *window, GLint w, GLint h) status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SendEventToEventTarget Failed"); - DEBUG_MSG_POETZSCH (("Size %d visible %d\n", window->id, IsWindowVisible (window->window))); + DEBUG_MSG_POETZSCH (("Size %d visible %d\n", window->BltInfo.Base.id, IsWindowVisible (window->window))); /* save the new size */ - window->width = w; - window->height = h; + window->BltInfo.width = w; + window->BltInfo.height = h; } void @@ -602,8 +605,11 @@ renderspu_SystemShowWindow(WindowInfo *window, GLboolean showIt) status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed"); } - /* Save the new value */ - window->visible = showIt; +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0); } void @@ -611,11 +617,13 @@ renderspu_SystemMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context) { Boolean result; -/* DEBUG_MSG_POETZSCH (("makecurrent %d: \n", window->id));*/ +/* DEBUG_MSG_POETZSCH (("makecurrent %d: \n", window->BltInfo.Base.id));*/ CRASSERT(render_spu.ws.aglSetCurrentContext); //crDebug( "renderspu_SystemMakeCurrent( %x, %i, %x )", window, nativeWindow, context ); + nativeWindow = 0; + if(window && context) { CRASSERT(window->window); @@ -669,7 +677,7 @@ renderspu_SystemSwapBuffers(WindowInfo *window, GLint flags) crError("Render SPU (renderspu_SystemSwapBuffers): SwapBuffers got a null context from the window"); RTSemFastMutexRequest(render_spu.syncMutex); -// DEBUG_MSG_POETZSCH (("Swapped %d context %x visible: %d\n", window->id, context->context, IsWindowVisible (window->window))); +// DEBUG_MSG_POETZSCH (("Swapped %d context %x visible: %d\n", window->BltInfo.Base.id, context->context, IsWindowVisible (window->window))); if (context->visual && context->visual->visAttribs & CR_DOUBLE_BIT) render_spu.ws.aglSwapBuffers(context->context); @@ -695,7 +703,7 @@ renderspu_SystemSwapBuffers(WindowInfo *window, GLint flags) } } -void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, GLint* pRects) +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) { CRASSERT(window); CRASSERT(window->window); @@ -730,7 +738,7 @@ void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, GLint renderspu_SystemWindowApplyVisibleRegion(window); } -void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects) +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects) { /* Remember the visible region of the root window if there is one */ if (render_spu.hRootVisibleRegion) @@ -757,7 +765,7 @@ void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects) } /*Assumes that all regions are in the guest coordinates system*/ -void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) { ContextInfo *c = renderspuGetWindowContext(window); RgnHandle rgn; @@ -777,8 +785,8 @@ void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) * currently process. */ SetRectRgn(rgn, window->x, window->y, - window->x + window->width, - window->y + window->height); + window->x + window->BltInfo.width, + window->y + window->BltInfo.height); SectRgn(render_spu.hRootVisibleRegion, rgn, rgn); /* Because the clipping is done in the coordinate space of the OpenGL * window we have to remove the x/y position from the newly created @@ -790,7 +798,7 @@ void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) /* If there is not root clipping region is available, create a base * region with the size of the target window. This covers all * needed/possible space. */ - SetRectRgn(rgn, 0, 0, window->width, window->height); + SetRectRgn(rgn, 0, 0, window->BltInfo.width, window->BltInfo.height); } /* Now intersect the window clipping region with a additional region e.g. @@ -838,8 +846,8 @@ renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt, windowRect.left = window->x; windowRect.top = window->y; - windowRect.right = window->x + window->width; - windowRect.bottom = window->y + window->height; + windowRect.right = window->x + window->BltInfo.width; + windowRect.bottom = window->y + window->BltInfo.height; status = CreateNewWindow(winClass, winAttr, &windowRect, &window->window); CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateNewWindow Failed", GL_FALSE); @@ -848,7 +856,7 @@ renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt, CFStringRef title_string; title_string = CFStringCreateWithCStringNoCopy(NULL, window->title, kCFStringEncodingMacRoman, NULL); - SetWindowTitleWithCFString(window->window, title_string); + SetWindowTitleWithCFString(window->BltInfo.window, title_string); CFRelease(title_string); /* The parent has to be in its own group */ @@ -873,8 +881,22 @@ renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt, renderspu_SystemShowWindow(window, GL_TRUE); crDebug("Render SPU (renderspu_SystemVBoxCreateWindow): actual window (x, y, width, height): %d, %d, %d, %d", - window->x, window->y, window->width, window->height); + window->x, window->y, window->BltInfo.width, window->BltInfo.height); return GL_TRUE; } +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c index a3b7b157..f9492a2d 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,6 +21,9 @@ #include <iprt/string.h> #include <iprt/path.h> +#include <cr_string.h> +#include <cr_mem.h> + GLboolean renderspu_SystemInitVisual(VisualInfo *pVisInfo) { CRASSERT(pVisInfo); @@ -82,7 +85,7 @@ GLboolean renderspu_SystemVBoxCreateWindow(VisualInfo *pVisInfo, GLboolean fShow NativeNSViewRef pParentWin = (NativeNSViewRef)(uint32_t)render_spu_parent_window_id; #endif /* __LP64__ */ - cocoaViewCreate(&pWinInfo->window, pParentWin, pVisInfo->visAttribs); + cocoaViewCreate(&pWinInfo->window, pWinInfo, pParentWin, pVisInfo->visAttribs); if (fShowIt) renderspu_SystemShowWindow(pWinInfo, fShowIt); @@ -151,15 +154,22 @@ void renderspu_SystemShowWindow(WindowInfo *pWinInfo, GLboolean fShowIt) cocoaViewShow(pWinInfo->window, fShowIt); } -void renderspu_SystemMakeCurrent(WindowInfo *pWinInfo, GLint nativeWindow, ContextInfo *pCtxInfo) +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) { - CRASSERT(pWinInfo); - CRASSERT(pCtxInfo); + cocoaViewPresentComposition(window->window, pChangedEntry); +} +void renderspu_SystemMakeCurrent(WindowInfo *pWinInfo, GLint nativeWindow, ContextInfo *pCtxInfo) +{ /* if(pWinInfo->visual != pCtxInfo->visual)*/ /* printf ("visual mismatch .....................\n");*/ - cocoaViewMakeCurrentContext(pWinInfo->window, pCtxInfo->context); + nativeWindow = 0; + + if (pWinInfo && pCtxInfo) + cocoaViewMakeCurrentContext(pWinInfo->window, pCtxInfo->context); + else + cocoaViewMakeCurrentContext(NULL, NULL); } void renderspu_SystemSwapBuffers(WindowInfo *pWinInfo, GLint flags) @@ -169,53 +179,308 @@ void renderspu_SystemSwapBuffers(WindowInfo *pWinInfo, GLint flags) cocoaViewDisplay(pWinInfo->window); } -void renderspu_SystemWindowVisibleRegion(WindowInfo *pWinInfo, GLint cRects, GLint* paRects) +void renderspu_SystemWindowVisibleRegion(WindowInfo *pWinInfo, GLint cRects, const GLint* paRects) { CRASSERT(pWinInfo); cocoaViewSetVisibleRegion(pWinInfo->window, cRects, paRects); } -void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *paRects) +void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *pWinInfo) { } -void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *pWinInfo) +int renderspu_SystemInit() { + return VINF_SUCCESS; } -void renderspu_SystemFlush() +int renderspu_SystemTerm() { - cocoaFlush(); + CrGlslTerm(&render_spu.GlobalShaders); + return VINF_SUCCESS; } -void renderspu_SystemFinish() +static SPUNamedFunctionTable * renderspuFindEntry(SPUNamedFunctionTable *aFunctions, const char *pcszName) { - cocoaFinish(); + SPUNamedFunctionTable *pCur; + + for (pCur = aFunctions ; pCur->name != NULL ; pCur++) + { + if (!crStrcmp( pcszName, pCur->name ) ) + { + return pCur; + } + } + + AssertFailed(); + + return NULL; } -void renderspu_SystemBindFramebufferEXT(GLenum target, GLuint framebuffer) +typedef struct CR_RENDER_CTX_INFO { - cocoaBindFramebufferEXT(target, framebuffer); + ContextInfo * pContext; + WindowInfo * pWindow; +} CR_RENDER_CTX_INFO; + +void renderspuCtxInfoInitCurrent(CR_RENDER_CTX_INFO *pInfo) +{ + GET_CONTEXT(pCurCtx); + pInfo->pContext = pCurCtx; + pInfo->pWindow = pCurCtx->currentWindow; } -void renderspu_SystemCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) +void renderspuCtxInfoRestoreCurrent(CR_RENDER_CTX_INFO *pInfo) { - cocoaCopyPixels(x, y, width, height, type); + GET_CONTEXT(pCurCtx); + if (pCurCtx == pInfo->pContext && (!pCurCtx || pCurCtx->currentWindow == pInfo->pWindow)) + return; + renderspuPerformMakeCurrent(pInfo->pWindow, 0, pInfo->pContext); } -void renderspu_SystemGetIntegerv(GLenum pname, GLint * params) +GLboolean renderspuCtxSetCurrentWithAnyWindow(ContextInfo * pContext, CR_RENDER_CTX_INFO *pInfo) { - cocoaGetIntegerv(pname, params); + WindowInfo * window; + renderspuCtxInfoInitCurrent(pInfo); + + if (pInfo->pContext == pContext) + return GL_TRUE; + + window = pContext->currentWindow; + if (!window) + { + window = renderspuGetDummyWindow(pContext->BltInfo.Base.visualBits); + if (!window) + { + crWarning("renderspuGetDummyWindow failed"); + return GL_FALSE; + } + } + + Assert(window); + + renderspuPerformMakeCurrent(window, 0, pContext); + return GL_TRUE; } -void renderspu_SystemReadBuffer(GLenum mode) +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) { - cocoaReadBuffer(mode); + CRASSERT(fromContext != toContext); + + if (!CrGlslIsInited(&render_spu.GlobalShaders)) + { + CrGlslInit(&render_spu.GlobalShaders, &render_spu.blitterDispatch); + } + + if (fromContext) + { + if (CrGlslNeedsCleanup(&render_spu.GlobalShaders)) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(fromContext, &Info)) + { + CrGlslCleanup(&render_spu.GlobalShaders); + renderspuCtxInfoRestoreCurrent(&Info); + } + else + crWarning("renderspuCtxSetCurrentWithAnyWindow failed!"); + } + } + else + { + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + } + + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + + if (toContext) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(toContext, &Info)) + { + int rc = CrGlslProgGenAllNoAlpha(&render_spu.GlobalShaders); + if (!RT_SUCCESS(rc)) + crWarning("CrGlslProgGenAllNoAlpha failed, rc %d", rc); + + renderspuCtxInfoRestoreCurrent(&Info); + } + else + crWarning("renderspuCtxSetCurrentWithAnyWindow failed!"); + } +} + +AssertCompile(sizeof (GLhandleARB) == sizeof (void*)); + +static VBoxGLhandleARB crHndlSearchVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); +} + +static GLhandleARB crHndlSearchNative(VBoxGLhandleARB hVBox) +{ + return (GLhandleARB)((uintptr_t)hVBox); +} + +static VBoxGLhandleARB crHndlAcquireVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); } -void renderspu_SystemDrawBuffer(GLenum mode) +static GLhandleARB crHndlReleaseVBox(VBoxGLhandleARB hVBox) { - cocoaDrawBuffer(mode); + return (GLhandleARB)((uintptr_t)hVBox); } +static void SPU_APIENTRY renderspu_SystemDeleteObjectARB(VBoxGLhandleARB obj) +{ + GLhandleARB hNative = crHndlReleaseVBox(obj); + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + render_spu.pfnDeleteObject(hNative); +} + +static void SPU_APIENTRY renderspu_SystemGetAttachedObjectsARB( VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * pCount, VBoxGLhandleARB * obj ) +{ + GLhandleARB *paAttachments; + GLhandleARB hNative = crHndlSearchNative(containerObj); + GLsizei count, i; + + if (pCount) + *pCount = 0; + + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + paAttachments = crCalloc(maxCount * sizeof (*paAttachments)); + if (!paAttachments) + { + crWarning("crCalloc failed"); + return; + } + + render_spu.pfnGetAttachedObjects(hNative, maxCount, &count, paAttachments); + if (pCount) + *pCount = count; + if (count > maxCount) + { + crWarning("count too big"); + count = maxCount; + } + + for (i = 0; i < count; ++i) + { + obj[i] = crHndlSearchVBox(paAttachments[i]); + CRASSERT(obj[i]); + } + + crFree(paAttachments); +} + +static VBoxGLhandleARB SPU_APIENTRY renderspu_SystemGetHandleARB(GLenum pname) +{ + GLhandleARB hNative = render_spu.pfnGetHandle(pname); + VBoxGLhandleARB hVBox; + if (!hNative) + { + crWarning("pfnGetHandle failed"); + return 0; + } + hVBox = crHndlAcquireVBox(hNative); + CRASSERT(hVBox); + return hVBox; +} + +static void SPU_APIENTRY renderspu_SystemGetInfoLogARB( VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetInfoLog(hNative, maxLength, length, infoLog); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameterfv(hNative, pname, params); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameteriv(hNative, pname, params); +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + SPUNamedFunctionTable * pEntry; + + pEntry = renderspuFindEntry(aFunctions, "DeleteObjectARB"); + if (pEntry) + { + render_spu.pfnDeleteObject = (PFNDELETE_OBJECT)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemDeleteObjectARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetAttachedObjectsARB"); + if (pEntry) + { + render_spu.pfnGetAttachedObjects = (PFNGET_ATTACHED_OBJECTS)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetAttachedObjectsARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetHandleARB"); + if (pEntry) + { + render_spu.pfnGetHandle = (PFNGET_HANDLE)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetHandleARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetInfoLogARB"); + if (pEntry) + { + render_spu.pfnGetInfoLog = (PFNGET_INFO_LOG)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetInfoLogARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterfvARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameterfv = (PFNGET_OBJECT_PARAMETERFV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterfvARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterivARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameteriv = (PFNGET_OBJECT_PARAMETERIV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterivARB; + } + + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h index 2380f289..546f98b6 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2011 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,9 +21,14 @@ #include <iprt/cdefs.h> #include <VBox/VBoxCocoa.h> #include <OpenGL/OpenGL.h> +#include <cr_vreg.h> +#include <cr_compositor.h> + RT_C_DECLS_BEGIN +struct WindowInfo; + ADD_COCOA_NATIVE_REF(NSView); ADD_COCOA_NATIVE_REF(NSOpenGLContext); @@ -32,7 +37,7 @@ void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, Na void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx); /* View management */ -void cocoaViewCreate(NativeNSViewRef *ppView, NativeNSViewRef pParentView, GLbitfield fVisParams); +void cocoaViewCreate(NativeNSViewRef *ppView, struct WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams); void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView); void cocoaViewDestroy(NativeNSViewRef pView); void cocoaViewDisplay(NativeNSViewRef pView); @@ -42,16 +47,8 @@ void cocoaViewSetSize(NativeNSViewRef pView, int w, int h); void cocoaViewGetGeometry(NativeNSViewRef pView, int *pX, int *pY, int *pW, int *pH); void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx); -void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, GLint* paRects); - -/* OpenGL wrapper */ -void cocoaFlush(void); -void cocoaFinish(void); -void cocoaBindFramebufferEXT(GLenum target, GLuint framebuffer); -void cocoaCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); -void cocoaGetIntegerv(GLenum pname, GLint *params); -void cocoaReadBuffer(GLenum mode); -void cocoaDrawBuffer(GLenum mode); +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint* paRects); +void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry); RT_C_DECLS_END diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m index eb2e5787..db2c00c8 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2011 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -18,6 +18,7 @@ #include "renderspu_cocoa_helper.h" #import <Cocoa/Cocoa.h> +#undef PVM #include "chromium.h" /* For the visual bits of chromium */ @@ -25,6 +26,17 @@ #include <iprt/string.h> #include <iprt/mem.h> #include <iprt/time.h> +#include <iprt/assert.h> + +#include <cr_vreg.h> +#include <cr_error.h> +#include <cr_blitter.h> +#ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +# include <cr_pixeldata.h> +#endif + + +#include "renderspu.h" /** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper * @@ -74,6 +86,7 @@ /* Debug macros */ #define FBO 1 /* Disable this to see how the output is without the FBO in the middle of the processing chain. */ #if 0 +# define CR_RENDER_FORCE_PRESENT_MAIN_THREAD /* force present schedule to main thread */ # define SHOW_WINDOW_BACKGROUND 1 /* Define this to see the window background even if the window is clipped */ # define DEBUG_VERBOSE /* Define this to get some debug info about the messages flow. */ #endif @@ -81,9 +94,16 @@ #ifdef DEBUG_misha # define DEBUG_MSG(text) \ printf text +# define DEBUG_WARN(text) do { \ + crWarning text ; \ + Assert(0); \ + } while (0) #else # define DEBUG_MSG(text) \ do {} while (0) +# define DEBUG_WARN(text) do { \ + crWarning text ; \ + } while (0) #endif #ifdef DEBUG_VERBOSE @@ -159,6 +179,114 @@ } \ while(0); +static NSOpenGLContext * vboxCtxGetCurrent() +{ + GET_CONTEXT(pCtxInfo); + if (pCtxInfo) + { + Assert(pCtxInfo->context); + return pCtxInfo->context; + } + + return nil; +} + +static bool vboxCtxSyncCurrentInfo() +{ + GET_CONTEXT(pCtxInfo); + NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; + NSView *pView = pCtx ? [pCtx view] : nil; + bool fAdjusted = false; + if (pCtxInfo) + { + WindowInfo *pWinInfo = pCtxInfo->currentWindow; + Assert(pWinInfo); + if (pCtxInfo->context != pCtx + || pWinInfo->window != pView) + { + renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo); + fAdjusted = true; + } + } + else + { + if (pCtx) + { + [NSOpenGLContext clearCurrentContext]; + fAdjusted = true; + } + } + + return fAdjusted; +} + +typedef struct VBOX_CR_RENDER_CTX_INFO +{ + bool fIsValid; + NSOpenGLContext *pCtx; + NSView *pView; +} VBOX_CR_RENDER_CTX_INFO, *PVBOX_CR_RENDER_CTX_INFO; + +static void vboxCtxEnter(NSOpenGLContext*pCtx, PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + NSOpenGLContext *pOldCtx = vboxCtxGetCurrent(); + NSView *pOldView = (pOldCtx ? [pOldCtx view] : nil); + NSView *pView = [pCtx view]; + bool fNeedCtxSwitch = (pOldCtx != pCtx || pOldView != pView); + Assert(pCtx); + // Assert(pOldCtx == m_pGLCtx); + // Assert(pOldView == self); + // Assert(fNeedCtxSwitch); + if (fNeedCtxSwitch) + { + if(pOldCtx != nil) + glFlush(); + + [pCtx makeCurrentContext]; + + pCtxInfo->fIsValid = true; + pCtxInfo->pCtx = pOldCtx; + pCtxInfo->pView = pView; + } + else + { + pCtxInfo->fIsValid = false; + } +} + +static void vboxCtxLeave(PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + if (pCtxInfo->fIsValid) + { + NSOpenGLContext *pOldCtx = pCtxInfo->pCtx; + NSView *pOldView = pCtxInfo->pView; + + glFlush(); + if (pOldCtx != nil) + { + if ([pOldCtx view] != pOldView) + { + [pOldCtx setView: pOldView]; + } + + [pOldCtx makeCurrentContext]; + +#ifdef DEBUG + { + NSOpenGLContext *pTstOldCtx = [NSOpenGLContext currentContext]; + NSView *pTstOldView = (pTstOldCtx ? [pTstOldCtx view] : nil); + Assert(pTstOldCtx == pOldCtx); + Assert(pTstOldView == pOldView); + } +#endif + } + else + { + [NSOpenGLContext clearCurrentContext]; + } + } +} + /** Custom OpenGL context class. * * This implementation doesn't allow to set a view to the @@ -193,25 +321,12 @@ NSOpenGLContext *m_pSharedGLCtx; RTTHREAD mThread; -#ifdef FBO GLuint m_FBOId; - /* FBO handling */ - GLuint m_FBOTexBackId; - GLuint m_FBOTexFrontId; - GLuint m_FBOAttBackId; - GLuint m_FBOAttFrontId; - GLuint m_FBODepthStencilPackedId; - NSSize m_FBOTexSize; - - bool m_fFrontDrawing; -#endif /** The corresponding dock tile view of this OpenGL view & all helper * members. */ DockOverlayView *m_DockTileView; - GLuint m_FBOThumbId; - GLuint m_FBOThumbTexId; GLfloat m_FBOThumbScaleX; GLfloat m_FBOThumbScaleY; uint64_t m_uiDockUpdateTime; @@ -225,9 +340,17 @@ NSSize m_Size; /** This is necessary for clipping on the root window */ - NSPoint m_RootShift; -} -- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView*)pParentView; + NSRect m_RootRect; + float m_yInvRootOffset; + + CR_BLITTER *m_pBlitter; + WindowInfo *m_pWinInfo; + bool m_fNeedViewportUpdate; + bool m_fNeedCtxUpdate; + bool m_fDataVisible; + bool m_fEverSized; +} +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView*)pParentView winInfo:(WindowInfo*)pWinInfo; - (void)setGLCtx:(NSOpenGLContext*)pCtx; - (NSOpenGLContext*)glCtx; @@ -238,28 +361,30 @@ - (void)setPos:(NSPoint)pos; - (NSPoint)pos; +- (bool)isEverSized; - (void)setSize:(NSSize)size; - (NSSize)size; -- (void)updateViewport; -- (void)reshape; +- (void)updateViewportCS; +- (void)vboxReshapePerform; +- (void)vboxReshapeOnResizePerform; +- (void)vboxReshapeOnReparentPerform; -- (void)createFBO; -- (void)deleteFBO; +- (void)createDockTile; +- (void)deleteDockTile; -- (bool)isCurrentFBO; -- (void)updateFBO; - (void)makeCurrentFBO; - (void)swapFBO; -- (void)flushFBO; -- (void)stateInfo:(GLenum)pname withParams:(GLint*)params; -- (void)finishFBO; -- (void)bindFBO:(GLenum)target withFrameBuffer:(GLuint)framebuffer; -- (void)tryDraw; -- (void)renderFBOToView; -- (void)renderFBOToDockTile; +- (void)vboxTryDraw; +- (void)vboxTryDrawUI; +- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR*)pCompositor; +- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor; +- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor; +- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor; +- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY*)pChangedEntry; +- (void)vboxBlitterSyncWindow; - (void)clearVisibleRegions; -- (void)setVisibleRegions:(GLint)cRects paRects:(GLint*)paRects; +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint*)paRects; - (NSView*)dockTileScreen; - (void)reshapeDockTile; @@ -465,7 +590,7 @@ { DEBUG_MSG(("OCTX(%p): setView: new view: %p\n", (void*)self, (void*)view)); -#ifdef FBO +#if 1 /* def FBO */ m_pView = view;; #else [super setView: view]; @@ -474,7 +599,7 @@ -(NSView*)view { -#ifdef FBO +#if 1 /* def FBO */ return m_pView; #else return [super view]; @@ -597,10 +722,16 @@ /* Reposition this window with the help of the OverlayView. Perform the * call in the OpenGL thread. */ /* - [m_pOverlayView performSelector:@selector(reshape) onThread:m_Thread withObject:nil waitUntilDone:YES]; + [m_pOverlayView performSelector:@selector(vboxReshapePerform) onThread:m_Thread withObject:nil waitUntilDone:YES]; */ - [m_pOverlayView reshape]; + if ([m_pOverlayView isEverSized]) + { + if([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } } - (void)parentWindowChanged:(NSWindow*)pWindow @@ -622,10 +753,17 @@ /* Reshape the overlay view after a short waiting time to let the main * window resize itself properly. */ /* - [m_pOverlayView performSelector:@selector(reshape) withObject:nil afterDelay:0.2]; - [NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(reshape) userInfo:nil repeats:NO]; + [m_pOverlayView performSelector:@selector(vboxReshapePerform) withObject:nil afterDelay:0.2]; + [NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(vboxReshapePerform) userInfo:nil repeats:NO]; */ - [m_pOverlayView reshape]; + + if ([m_pOverlayView isEverSized]) + { + if([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } } } @@ -638,30 +776,27 @@ ********************************************************************************/ @implementation OverlayView -- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView*)pParentView +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView*)pParentView winInfo:(WindowInfo*)pWinInfo { m_pParentView = pParentView; /* Make some reasonable defaults */ m_pGLCtx = nil; m_pSharedGLCtx = nil; mThread = aThread; -#ifdef FBO m_FBOId = 0; - m_FBOTexBackId = 0; - m_FBOTexFrontId = 0; - m_FBOAttBackId = GL_COLOR_ATTACHMENT0_EXT; - m_FBOAttFrontId = GL_COLOR_ATTACHMENT1_EXT; - m_FBODepthStencilPackedId = 0; - m_FBOTexSize = NSZeroSize; -#endif - m_FBOThumbId = 0; - m_FBOThumbTexId = 0; m_cClipRects = 0; m_paClipRects = NULL; m_Pos = NSZeroPoint; m_Size = NSMakeSize(1, 1); - m_RootShift = NSZeroPoint; - + m_RootRect = NSMakeRect(0, 0, m_Size.width, m_Size.height); + m_yInvRootOffset = 0; + m_pBlitter = nil; + m_pWinInfo = pWinInfo; + m_fNeedViewportUpdate = true; + m_fNeedCtxUpdate = true; + m_fDataVisible = false; + m_fEverSized = false; + self = [super initWithFrame:frame]; DEBUG_MSG(("OVIW(%p): init OverlayView\n", (void*)self)); @@ -671,15 +806,10 @@ - (void)cleanupData { - [self deleteFBO]; - - if (m_pGLCtx) - { - if ([m_pGLCtx view] == self) - [m_pGLCtx clearDrawable]; - - m_pGLCtx = nil; - } + [self deleteDockTile]; + + [self setGLCtx:nil]; + if (m_pSharedGLCtx) { if ([m_pSharedGLCtx view] == self) @@ -688,6 +818,12 @@ [m_pSharedGLCtx release]; m_pSharedGLCtx = nil; + + CrBltTerm(m_pBlitter); + + RTMemFree(m_pBlitter); + + m_pBlitter = nil; } [self clearVisibleRegions]; @@ -704,7 +840,7 @@ - (void)drawRect:(NSRect)aRect { - /* Do nothing */ + [self vboxTryDrawUI]; } - (void)setGLCtx:(NSOpenGLContext*)pCtx @@ -715,9 +851,15 @@ /* ensure the context drawable is cleared to avoid holding a reference to inexistent view */ if (m_pGLCtx) + { [m_pGLCtx clearDrawable]; + [m_pGLCtx release]; + /*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + } m_pGLCtx = pCtx; + if (pCtx) + [pCtx retain]; } - (NSOpenGLContext*)glCtx @@ -755,7 +897,16 @@ m_Pos = pos; - [self reshape]; + if (m_fEverSized) + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + + /* we need to redwar on regions change, however the compositor now is cleared + * because all compositor&window data-related modifications are performed with compositor cleared + * the renderspu client will re-set the compositor after modifications are complete + * this way we indicate renderspu generic code not to ignore the empty compositor */ + /* generally this should not be needed for setPos because compositor should not be zeroed with it, + * in any way setting this flag here should not hurt as it will be re-set on next present */ + m_pWinInfo->fCompositorPresentEmpty = GL_TRUE; } - (NSPoint)pos @@ -763,26 +914,29 @@ return m_Pos; } +- (bool)isEverSized +{ + return m_fEverSized; +} + - (void)setSize:(NSSize)size { + NSOpenGLContext *pCurCtx; + NSView *pCurView; m_Size = size; + + m_fEverSized = true; -#ifdef FBO - if (m_FBOId) - { - DEBUG_MSG(("OVIW(%p): setSize: new size: %dx%d\n", (void*)self, (int)size.width, (int)size.height)); - [self reshape]; - [self updateFBO]; - /* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */ - [self updateViewport]; - } - else -#endif - { - DEBUG_MSG(("OVIW(%p): setSize (no FBO): new size: %dx%d\n", (void*)self, (int)size.width, (int)size.height)); - [self reshape]; - [self updateFBO]; - } + DEBUG_MSG(("OVIW(%p): setSize: new size: %dx%d\n", (void*)self, (int)size.width, (int)size.height)); + [self performSelectorOnMainThread:@selector(vboxReshapeOnResizePerform) withObject:nil waitUntilDone:NO]; + + /* we need to redwar on regions change, however the compositor now is cleared + * because all compositor&window data-related modifications are performed with compositor cleared + * the renderspu client will re-set the compositor after modifications are complete + * this way we indicate renderspu generic code not to ignore the empty compositor */ + /* generally this should not be needed for setSize because compositor should not be zeroed with it, + * in any way setting this flag here should not hurt as it will be re-set on next present */ + m_pWinInfo->fCompositorPresentEmpty = GL_TRUE; } - (NSSize)size @@ -790,44 +944,47 @@ return m_Size; } -- (void)updateViewport +- (void)updateViewportCS { - NSRect r; - DEBUG_MSG(("OVIW(%p): updateViewport\n", (void*)self)); -#ifdef FBO - if (m_pSharedGLCtx) + /* Update the viewport for our OpenGL view */ + [m_pSharedGLCtx update]; + + [self vboxBlitterSyncWindow]; + + /* Clear background to transparent */ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +} + +- (void)vboxReshapeOnResizePerform +{ + [self vboxReshapePerform]; + + [self createDockTile]; + /* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */ + m_fNeedViewportUpdate = true; +#if 0 + pCurCtx = [NSOpenGLContext currentContext]; + if (pCurCtx && pCurCtx == m_pGLCtx && (pCurView = [pCurCtx view]) == self) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + else { - /* Update the viewport for our OpenGL view */ - DEBUG_MSG(("OVIW(%p): makeCurrent (shared) %p\n", (void*)self, (void*)m_pSharedGLCtx)); - [m_pSharedGLCtx makeCurrentContext]; - [m_pSharedGLCtx update]; - - r = [self frame]; - /* Setup all matrices */ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glViewport(0, 0, r.size.width, r.size.height); - glOrtho(0, r.size.width, 0, r.size.height, -1, 1); - DEBUG_MSG_1(("OVIW(%p): frame[%i, %i, %i, %i]\n", (void*)self, (int)r.origin.x, (int)r.origin.x, (int)r.size.width, (int)r.size.height)); - DEBUG_MSG_1(("OVIW(%p): m_Pos(%i,%i) m_Size(%i,%i)\n", (void*)self, (int)m_Pos.x, (int)m_Pos.y, (int)m_Size.width, (int)m_Size.height)); - DEBUG_MSG_1(("OVIW(%p): m_RootShift(%i, %i)\n", (void*)self, (int)m_RootShift.x, (int)m_RootShift.y)); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - /* Clear background to transparent */ - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - DEBUG_MSG(("OVIW(%p): makeCurrent (non shared) %p\n", (void*)self, (void*)m_pGLCtx)); - [m_pGLCtx makeCurrentContext]; + /* do it in a lazy way */ + m_fNeedCtxUpdate = true; } #endif } -- (void)reshape +- (void)vboxReshapeOnReparentPerform +{ + [self createDockTile]; +} + +- (void)vboxReshapePerform { NSRect parentFrame = NSZeroRect; NSPoint parentPos = NSZeroPoint; @@ -835,38 +992,60 @@ NSRect childFrame = NSZeroRect; NSRect newFrame = NSZeroRect; - DEBUG_MSG(("OVIW(%p): reshape\n", (void*)self)); - - /* Getting the right screen coordinates of the parents frame is a little bit - * complicated. */ + DEBUG_MSG(("OVIW(%p): vboxReshapePerform\n", (void*)self)); + parentFrame = [m_pParentView frame]; - parentPos = [[m_pParentView window] convertBaseToScreen:[[m_pParentView superview] convertPointToBase:NSMakePoint(parentFrame.origin.x, parentFrame.origin.y + parentFrame.size.height)]]; - parentFrame.origin.x = parentPos.x; - parentFrame.origin.y = parentPos.y; - - /* Calculate the new screen coordinates of the overlay window. */ + DEBUG_MSG(("FIXED parentFrame [%f:%f], [%f:%f]\n", parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height)); + parentPos = parentFrame.origin; + parentPos.y += parentFrame.size.height; + DEBUG_MSG(("FIXED(view) parentPos [%f:%f]\n", parentPos.x, parentPos.y)); + parentPos = [m_pParentView convertPoint:parentPos toView:nil]; + DEBUG_MSG(("FIXED parentPos(win) [%f:%f]\n", parentPos.x, parentPos.y)); + parentPos = [[m_pParentView window] convertBaseToScreen:parentPos]; + DEBUG_MSG(("FIXED parentPos(screen) [%f:%f]\n", parentPos.x, parentPos.y)); + parentFrame.origin = parentPos; + childPos = NSMakePoint(m_Pos.x, m_Pos.y + m_Size.height); - childPos = [[m_pParentView window] convertBaseToScreen:[[m_pParentView superview] convertPointToBase:childPos]]; - - /* Make a frame out of it. */ + DEBUG_MSG(("FIXED(view) childPos [%f:%f]\n", childPos.x, childPos.y)); + childPos = [m_pParentView convertPoint:childPos toView:nil]; + DEBUG_MSG(("FIXED(win) childPos [%f:%f]\n", childPos.x, childPos.y)); + childPos = [[m_pParentView window] convertBaseToScreen:childPos]; + DEBUG_MSG(("FIXED childPos(screen) [%f:%f]\n", childPos.x, childPos.y)); childFrame = NSMakeRect(childPos.x, childPos.y, m_Size.width, m_Size.height); + DEBUG_MSG(("FIXED childFrame [%f:%f], [%f:%f]\n", childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height)); /* We have to make sure that the overlay window will not be displayed out * of the parent window. So intersect both frames & use the result as the new * frame for the window. */ newFrame = NSIntersectionRect(parentFrame, childFrame); - /* Later we have to correct the texture position in the case the window is - * out of the parents window frame. So save the shift values for later use. */ - if (parentFrame.origin.x > childFrame.origin.x) - m_RootShift.x = parentFrame.origin.x - childFrame.origin.x; - else - m_RootShift.x = 0; - if (parentFrame.origin.y > childFrame.origin.y) - m_RootShift.y = parentFrame.origin.y - childFrame.origin.y; - else - m_RootShift.y = 0; + DEBUG_MSG(("[%#p]: parentFrame pos[%f : %f] size[%f : %f]\n", + (void*)self, + parentFrame.origin.x, parentFrame.origin.y, + parentFrame.size.width, parentFrame.size.height)); + DEBUG_MSG(("[%#p]: childFrame pos[%f : %f] size[%f : %f]\n", + (void*)self, + childFrame.origin.x, childFrame.origin.y, + childFrame.size.width, childFrame.size.height)); + + DEBUG_MSG(("[%#p]: newFrame pos[%f : %f] size[%f : %f]\n", + (void*)self, + newFrame.origin.x, newFrame.origin.y, + newFrame.size.width, newFrame.size.height)); + /* Later we have to correct the texture position in the case the window is + * out of the parents window frame. So save the shift values for later use. */ + m_RootRect.origin.x = newFrame.origin.x - childFrame.origin.x; + m_RootRect.origin.y = childFrame.size.height + childFrame.origin.y - (newFrame.size.height + newFrame.origin.y); + m_RootRect.size = newFrame.size; + m_yInvRootOffset = newFrame.origin.y - childFrame.origin.y; + + DEBUG_MSG(("[%#p]: m_RootRect pos[%f : %f] size[%f : %f]\n", + (void*)self, + m_RootRect.origin.x, m_RootRect.origin.y, + m_RootRect.size.width, m_RootRect.size.height)); + + /* NSScrollView *pScrollView = [[[m_pParentView window] contentView] enclosingScrollView]; if (pScrollView) @@ -888,220 +1067,37 @@ [self reshapeDockTile]; /* Make sure the context is updated according */ - [self updateViewport]; -} - -- (void)createFBO -{ - GLint oldTexId = 0; - GLint oldFBId = 0; - NSView *pDockScreen = nil; - GLint maxTexSize = 0; - GLfloat imageAspectRatio = 0; - GLint filter = GL_NEAREST; - - [self deleteFBO]; - -#ifdef FBO - DEBUG_MSG(("OVIW(%p): createFBO\n", (void*)self)); - - glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &oldTexId); - glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &oldFBId); - - /* If not previously setup generate IDs for FBO and its associated texture. */ - if (!m_FBOId) - { - /* Make sure the framebuffer extension is supported */ - const GLubyte* strExt; - GLboolean isFBO; - /* Get the extension name string. It is a space-delimited list of the - * OpenGL extensions that are supported by the current renderer. */ - strExt = glGetString(GL_EXTENSIONS); - isFBO = gluCheckExtension((const GLubyte*)"GL_EXT_framebuffer_object", strExt); - if (!isFBO) - { - DEBUG_MSG(("Your system does not support the GL_EXT_framebuffer_object extension\n")); - } - isFBO = gluCheckExtension((const GLubyte*)"GL_EXT_framebuffer_blit", strExt); - if (!isFBO) - { - DEBUG_MSG(("Your system does not support the GL_EXT_framebuffer_blit extension\n")); - } - - /* Create FBO object */ - glGenFramebuffersEXT(1, &m_FBOId); - /* & the texture as well the depth/stencil render buffer */ - glGenTextures(1, &m_FBOTexBackId); - glGenTextures(1, &m_FBOTexFrontId); - DEBUG_MSG(("OVIW(%p): gen numbers: FBOId=%d FBOTexBackId=%d FBOTexFrontId=%d\n", (void*)self, m_FBOId, m_FBOTexBackId, m_FBOTexFrontId)); - - glGenRenderbuffersEXT(1, &m_FBODepthStencilPackedId); - } - - m_FBOTexSize = m_Size; - /* Bind to FBO */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBOId); - - /* - glEnable(GL_TEXTURE_RECTANGLE_ARB); - */ - - imageAspectRatio = m_FBOTexSize.width / m_FBOTexSize.height; - - /* Sanity check against maximum OpenGL texture size. If bigger adjust to - * maximum possible size while maintain the aspect ratio. */ - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); - if (m_FBOTexSize.width > maxTexSize || m_FBOTexSize.height > maxTexSize) + /* [self updateViewport]; */ + if (m_pSharedGLCtx) { - filter = GL_LINEAR; - if (imageAspectRatio > 1) - { - m_FBOTexSize.width = maxTexSize; - m_FBOTexSize.height = maxTexSize / imageAspectRatio; - } - else - { - m_FBOTexSize.width = maxTexSize * imageAspectRatio; - m_FBOTexSize.height = maxTexSize; - } + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self updateViewportCS]; + + vboxCtxLeave(&CtxInfo); } +} - DEBUG_MSG(("OVIW(%p): tex size is: %dx%d\n", (void*)self, (int)m_FBOTexSize.width, (int)m_FBOTexSize.height)); - - /* Initialize FBO Textures */ - /* The GPUs like the GL_BGRA / GL_UNSIGNED_INT_8_8_8_8_REV combination - * others are also valid, but might incur a costly software translation. */ - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_FBOTexBackId); - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, m_FBOTexSize.width, m_FBOTexSize.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_FBOTexFrontId); - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, m_FBOTexSize.width, m_FBOTexSize.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); - - /* Now attach the textures to the FBO as its color destinations */ - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, m_FBOAttBackId, GL_TEXTURE_RECTANGLE_ARB, m_FBOTexBackId, 0); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, m_FBOAttFrontId, GL_TEXTURE_RECTANGLE_ARB, m_FBOTexFrontId, 0); - - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_FBODepthStencilPackedId); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, m_FBOTexSize.width, m_FBOTexSize.height); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_FBODepthStencilPackedId); - - /* Bind the FBOs for reading and drawing. */ - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_FBOId); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_FBOId); - - /* Explicitly clear the textures otherwise they would contain old memory stuff. */ - glDrawBuffer(m_FBOAttBackId); - glClear(GL_COLOR_BUFFER_BIT); - glDrawBuffer(m_FBOAttFrontId); - glClear(GL_COLOR_BUFFER_BIT); - - /* Now initially reading/drawing to the back buffer. */ - glReadBuffer(m_FBOAttBackId); - glDrawBuffer(m_FBOAttBackId); - - /* Make sure the FBO was created successfully. */ - if (GL_FRAMEBUFFER_COMPLETE_EXT != glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)) - DEBUG_MSG(("OVIW(%p): Framebuffer Object creation or update failed!\n", (void*)self)); - -// glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, oldTexId); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)oldFBId ? (GLuint)oldFBId : m_FBOId); - - /* Is there a dock tile preview enabled in the GUI? If so setup a +- (void)createDockTile +{ + NSView *pDockScreen = nil; + [self deleteDockTile]; + + /* Is there a dock tile preview enabled in the GUI? If so setup a * additional thumbnail view for the dock tile. */ - pDockScreen = [self dockTileScreen]; - if (pDockScreen) + pDockScreen = [self dockTileScreen]; + if (pDockScreen) { - if (!m_FBOThumbId) - { - glGenFramebuffersEXT(1, &m_FBOThumbId); - glGenTextures(1, &m_FBOThumbTexId); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBOThumbId); - /* Initialize FBO Texture */ - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_FBOThumbTexId); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); - - /* The GPUs like the GL_BGRA / GL_UNSIGNED_INT_8_8_8_8_REV combination - * others are also valid, but might incur a costly software translation. */ - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, m_FBOTexSize.width * m_FBOThumbScaleX, m_FBOTexSize.height * m_FBOThumbScaleY, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); - - /* Now attach texture to the FBO as its color destination */ - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, m_FBOThumbTexId, 0); - - /* Make sure the FBO was created successfully. */ - if (GL_FRAMEBUFFER_COMPLETE_EXT != glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)) - DEBUG_MSG(("OVIW(%p): Framebuffer \"Thumb\" Object creation or update failed!\n", (void*)self)); - - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, oldTexId); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint)oldFBId ? (GLuint)oldFBId : m_FBOId); - m_DockTileView = [[DockOverlayView alloc] init]; [self reshapeDockTile]; [pDockScreen addSubview:m_DockTileView]; } - - /* Initialize with one big visual region over the full size */ - [self clearVisibleRegions]; - m_cClipRects = 1; - m_paClipRects = (GLint*)RTMemAlloc(sizeof(GLint) * 4); - m_paClipRects[0] = 0; - m_paClipRects[1] = 0; - m_paClipRects[2] = m_FBOTexSize.width; - m_paClipRects[3] = m_FBOTexSize.height; -#endif } -- (void)deleteFBO +- (void)deleteDockTile { - DEBUG_MSG(("OVIW(%p): deleteFBO\n", (void*)self)); - - if (m_pSharedGLCtx) - { - DEBUG_MSG(("OVIW(%p): makeCurrent (shared) %p\n", (void*)self, (void*)m_pSharedGLCtx)); - [m_pSharedGLCtx makeCurrentContext]; - [m_pSharedGLCtx update]; - - glEnable(GL_TEXTURE_RECTANGLE_ARB); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - } - - if (m_pGLCtx) - { - DEBUG_MSG(("OVIW(%p): makeCurrent (non shared) %p\n", (void*)self, (void*)m_pGLCtx)); - [m_pGLCtx makeCurrentContext]; - -#ifdef FBO - if (m_FBODepthStencilPackedId > 0) - { - glDeleteRenderbuffersEXT(1, &m_FBODepthStencilPackedId); - m_FBODepthStencilPackedId = 0; - } - if (m_FBOTexBackId > 0) - { - glDeleteTextures(1, &m_FBOTexBackId); - m_FBOTexBackId = 0; - } - if (m_FBOTexFrontId > 0) - { - glDeleteTextures(1, &m_FBOTexFrontId); - m_FBOTexFrontId = 0; - } - if (m_FBOId > 0) - { - if ([self isCurrentFBO]) - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - - glDeleteFramebuffersEXT(1, &m_FBOId); - m_FBOId = 0; - } -#endif - } - - if (m_DockTileView != nil) + if (m_DockTileView != nil) { [m_DockTileView removeFromSuperview]; [m_DockTileView release]; @@ -1109,32 +1105,10 @@ } } -- (void)updateFBO -{ - DEBUG_MSG(("OVIW(%p): updateFBO\n", (void*)self)); - - [self makeCurrentFBO]; - - if (m_pGLCtx) - { -#ifdef FBO - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - [self createFBO]; - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBOId); -#endif - [m_pGLCtx update]; - } -} - - (void)makeCurrentFBO { DEBUG_MSG(("OVIW(%p): makeCurrentFBO\n", (void*)self)); -#ifdef FBO - DEBUG_MSG(("OVIW(%p): FBOId=%d CTX=%p\n", (void*)self, m_FBOId, (void*)m_pGLCtx)); - if([NSOpenGLContext currentContext] != 0) - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBOId); -#endif if (m_pGLCtx) { if ([m_pGLCtx view] != self) @@ -1151,352 +1125,325 @@ { [m_pGLCtx makeCurrentContext]; CHECK_GL_ERROR(); - /* - [m_pGLCtx update]; - */ + if (m_fNeedCtxUpdate == true) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } } + + if (!m_FBOId) + { + glGenFramebuffersEXT(1, &m_FBOId); + Assert(m_FBOId); + } + } -#ifdef FBO - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FBOId); -#endif } -- (bool)isCurrentFBO +- (bool)vboxSharedCtxCreate { -#ifdef FBO - GLint curFBOId = 0; - - glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBOId); - DEBUG_MSG_1(("OVIW(%p): isCurrentFBO: curFBOId=%d FBOId=%d\n", (void*)self, curFBOId, m_FBOId)); - return (GLuint)curFBOId == m_FBOId; -#else - return false; -#endif -} - -- (void)tryDraw -{ - if ([self lockFocusIfCanDraw]) + if (m_pSharedGLCtx) + return true; + + Assert(!m_pBlitter); + m_pBlitter = RTMemAlloc(sizeof (*m_pBlitter)); + if (!m_pBlitter) { - [self renderFBOToView]; - [self unlockFocus]; + DEBUG_WARN(("m_pBlitter allocation failed")); + return false; + } + + int rc = CrBltInit(m_pBlitter, NULL, false, false, &render_spu.GlobalShaders, &render_spu.blitterDispatch); + if (RT_SUCCESS(rc)) + { + DEBUG_MSG(("blitter created successfully for view 0x%p\n", (void*)self)); } + else + { + DEBUG_WARN(("CrBltInit failed, rc %d", rc)); + RTMemFree(m_pBlitter); + m_pBlitter = NULL; + return false; + } + + GLint opaque = 0; + /* Create a shared context out of the main context. Use the same pixel format. */ + NSOpenGLContext *pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:[(OverlayOpenGLContext*)m_pGLCtx openGLPixelFormat] shareContext:m_pGLCtx]; + + /* Set the new context as non opaque */ + [pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + /* Set this view as the drawable for the new context */ + [pSharedGLCtx setView: self]; + m_fNeedViewportUpdate = true; + + m_pSharedGLCtx = pSharedGLCtx; + + return true; } -- (void)swapFBO +- (void)vboxTryDraw { - GLint sw = 0; - GLint readFBOId = 0; - GLint drawFBOId = 0; - GLint readId = 0; - GLint drawId = 0; - - DEBUG_MSG(("OVIW(%p): swapFBO\n", (void*)self)); - -#ifdef FBO - /* Don't use flush buffers cause we are using FBOs here! */ - - /* Before we swap make sure everything is done (This is really - * important. Don't remove.) */ glFlush(); + + /* issue to the gui thread */ + [self setNeedsDisplay:YES]; +} + +- (void)vboxTryDrawUI +{ + const VBOXVR_SCR_COMPOSITOR *pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo); + if (!m_fDataVisible && !pCompositor) + return; - /* Fetch the current used read and draw buffers. */ - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFBOId); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBOId); - glGetIntegerv(GL_READ_BUFFER, &readId); - glGetIntegerv(GL_DRAW_BUFFER, &drawId); - - /* Do the swapping of our internal ids */ - sw = m_FBOTexFrontId; - m_FBOTexFrontId = m_FBOTexBackId; - m_FBOTexBackId = sw; - sw = m_FBOAttFrontId; - m_FBOAttFrontId = m_FBOAttBackId; - m_FBOAttBackId = sw; - - DEBUG_MSG_1(("read FBO: %d draw FBO: %d readId: %d drawId: %d\n", readFBOId, drawFBOId, readId, drawId)); - /* We also have to swap the real ids on the current context. */ - if ((GLuint)readFBOId == m_FBOId) + VBOXVR_SCR_COMPOSITOR TmpCompositor; + + if (pCompositor) { - if ((GLuint)readId == m_FBOAttFrontId) - glReadBuffer(m_FBOAttBackId); - if ((GLuint)readId == m_FBOAttBackId) - glReadBuffer(m_FBOAttFrontId); + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + renderspuVBoxCompositorRelease(m_pWinInfo); + if (![self vboxSharedCtxCreate]) + { + DEBUG_WARN(("vboxSharedCtxCreate failed\n")); + return; + } + + Assert(m_pSharedGLCtx); + + pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo); + Assert(!m_fDataVisible); + if (!pCompositor) + return; + } } - if ((GLuint)drawFBOId == m_FBOId) + else { - if ((GLuint)drawId == m_FBOAttFrontId) - glDrawBuffer(m_FBOAttBackId); - if ((GLuint)drawId == m_FBOAttBackId) - glDrawBuffer(m_FBOAttFrontId); + CrVrScrCompositorInit(&TmpCompositor, NULL); + pCompositor = &TmpCompositor; + } + + if ([self lockFocusIfCanDraw]) + { + [self vboxPresent:pCompositor]; + if (pCompositor != &TmpCompositor) + renderspuVBoxCompositorRelease(m_pWinInfo); + + [self unlockFocus]; + } + else + { + [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO]; } - - if (m_cClipRects) - [self tryDraw]; -#else - [m_pGLCtx flushBuffer]; -#endif } -- (void)flushFBO +- (void)swapFBO { - GLint drawId = 0; - GLint FBOId = 0; - - DEBUG_MSG(("OVIW(%p): flushFBO\n", (void*)self)); - - glFlush(); -#ifdef FBO - /* If at any time OpenGl operations where done in the front buffer, we need - * to reflect this in the FBO as well. This is something which on real - * hardware happens and unfortunately some applications rely on it (grrr ... Compiz). */ - if ( m_fFrontDrawing - && [self isCurrentFBO]) - { - /* Only reset if we aren't currently front. */ - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &FBOId); - glGetIntegerv(GL_DRAW_BUFFER, &drawId); - if (!( (GLuint)FBOId == m_FBOId - && (GLuint)drawId == m_FBOAttFrontId)) - m_fFrontDrawing = false; - if (m_cClipRects) - [self tryDraw]; - } -#endif + [m_pGLCtx flushBuffer]; } -- (void)finishFBO +- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR*)pCompositor { - DEBUG_MSG(("OVIW(%p): finishFBO\n", (void*)self)); + VBOX_CR_RENDER_CTX_INFO CtxInfo; + + DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void*)self)); + + Assert(pCompositor); - glFinish(); -#ifdef FBO - if (m_cClipRects && [self isCurrentFBO]) - [self tryDraw]; -#endif + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self vboxPresentCS:pCompositor]; + + vboxCtxLeave(&CtxInfo); } -- (void)stateInfo:(GLenum)pname withParams:(GLint*)params +- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor { - GLint test; -// DEBUG_MSG_1(("StateInfo requested: %d\n", pname)); - - glGetIntegerv(pname, params); -#ifdef FBO - switch(pname) - { - case GL_FRAMEBUFFER_BINDING_EXT: - case GL_READ_FRAMEBUFFER_BINDING: - case GL_READ_FRAMEBUFFER_EXT: - case GL_DRAW_FRAMEBUFFER_EXT: { - if ((GLuint)*params == m_FBOId) - *params = 0; - break; - } - case GL_READ_BUFFER: - { - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &test); - if ((GLuint)test == m_FBOId) + if ([m_pSharedGLCtx view] != self) { - if ((GLuint)*params == m_FBOAttFrontId) - *params = GL_FRONT; - else - if ((GLuint)*params == m_FBOAttBackId) - *params = GL_BACK; + DEBUG_MSG(("OVIW(%p): not current view of shared ctx! Switching ...\n", (void*)self)); + [m_pSharedGLCtx setView: self]; + m_fNeedViewportUpdate = true; } - break; - } - case GL_DRAW_BUFFER: - { - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &test); - if ((GLuint)test == m_FBOId) + + if (m_fNeedViewportUpdate) { - if ((GLuint)*params == m_FBOAttFrontId) - *params = GL_FRONT; - else - if ((GLuint)*params == m_FBOAttBackId) - *params = GL_BACK; + [self updateViewportCS]; + m_fNeedViewportUpdate = false; } - break; - } - } + + /* Render FBO content to the dock tile when necessary. */ + [self vboxPresentToDockTileCS:pCompositor]; + /* change to #if 0 to see thumbnail image */ +#if 1 + [self vboxPresentToViewCS:pCompositor]; +#else + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; #endif + + } } -- (void)readBuffer:(GLenum)mode +DECLINLINE(void) vboxNSRectToRect(const NSRect *pR, RTRECT *pRect) { -#ifdef FBO - /* - if ([self isCurrentFBO]) - */ - { - if (mode == GL_FRONT) - { - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_FBOId); - glReadBuffer(m_FBOAttFrontId); - } - else if (mode == GL_BACK) - { - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_FBOId); - glReadBuffer(m_FBOAttBackId); - } - else - glReadBuffer(mode); - } -#else - glReadBuffer(mode); -#endif + pRect->xLeft = (int)pR->origin.x; + pRect->yTop = (int)pR->origin.y; + pRect->xRight = (int)(pR->origin.x + pR->size.width); + pRect->yBottom = (int)(pR->origin.y + pR->size.height); } -- (void)drawBuffer:(GLenum)mode +DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) { -#ifdef FBO - /* - if ([self isCurrentFBO]) - */ - { - if (mode == GL_FRONT) - { - DEBUG_MSG(("OVIW(%p): front\n", (void*)self)); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_FBOId); - glDrawBuffer(m_FBOAttFrontId); - m_fFrontDrawing = true; - } - else if (mode == GL_BACK) - { - DEBUG_MSG(("OVIW(%p): back\n", (void*)self)); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_FBOId); - glDrawBuffer(m_FBOAttBackId); - } - else - { - DEBUG_MSG(("OVIW(%p): other: %d\n", (void*)self, mode)); - glDrawBuffer(mode); - } - } -#else - glDrawBuffer(mode); -#endif + pRect->xLeft = (int)(pR->origin.x / xStretch); + pRect->yTop = (int)(pR->origin.y / yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) / xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) / yStretch); } -- (void)bindFBO:(GLenum)target withFrameBuffer:(GLuint)framebuffer +DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) { -#ifdef FBO - if (framebuffer != 0) - glBindFramebufferEXT(target, framebuffer); - else - glBindFramebufferEXT(target, m_FBOId); -#else - glBindFramebufferEXT(target, framebuffer); -#endif + pRect->xLeft = (int)(pR->origin.x * xStretch); + pRect->yTop = (int)(pR->origin.y * yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) * xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) * yStretch); } -- (void)renderFBOToView +- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor { - GLint opaque = 0; - GLint i = 0; - GLint oldReadFBOId = 0; - GLint oldDrawFBOId = 0; - GLint oldReadId = 0; - GLint oldDrawId = 0; - - DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void*)self)); - -#ifdef FBO - - /* Fetch the current used read and draw buffers. */ - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &oldReadFBOId); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &oldDrawFBOId); - glGetIntegerv(GL_READ_BUFFER, &oldReadId); - glGetIntegerv(GL_DRAW_BUFFER, &oldDrawId); + NSRect r = [self frame]; + float xStretch, yStretch; + DEBUG_MSG(("OVIW(%p): rF2V frame: [%i, %i, %i, %i]\n", (void*)self, (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height)); - if (!m_pSharedGLCtx) - { - /* Create a shared context out of the main context. Use the same pixel format. */ - m_pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:[(OverlayOpenGLContext*)m_pGLCtx openGLPixelFormat] shareContext:m_pGLCtx]; - - /* Set the new context as non opaque */ - [m_pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; - /* Set this view as the drawable for the new context */ - [m_pSharedGLCtx setView: self]; - [self updateViewport]; - } - - if (m_pSharedGLCtx) +#if 1 /* Set to 0 to see the docktile instead of the real output */ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + m_fDataVisible = false; + + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) { - NSRect r = [self frame]; - DEBUG_MSG(("OVIW(%p): rF2V frame: [%i, %i, %i, %i]\n", (void*)self, (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height)); - - if (m_FBOTexFrontId > 0) + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) { - if ([m_pSharedGLCtx view] != self) - { - DEBUG_MSG(("OVIW(%p): not current view of shared ctx! Switching ...\n", (void*)self)); - [m_pSharedGLCtx setView: self]; - [self updateViewport]; + uint32_t i; + int rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + for (i = 0; i < cRegions; ++i) + { + const RTRECT * pSrcRect = &paSrcRegions[i]; + const RTRECT * pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + if (VBoxRectIsZero(&DstRect)) + continue; + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch, yStretch); + VBoxRectTranslate(&RestrictSrcRect, -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA); + + m_fDataVisible = true; + } + CrBltLeave(m_pBlitter); } - - [m_pSharedGLCtx makeCurrentContext]; - - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_FBOId); - glReadBuffer(m_FBOAttFrontId); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); - glDrawBuffer(GL_BACK); - - /* Render FBO content to the dock tile when necessary. */ - [self renderFBOToDockTile]; - -#if 1 /* Set to 0 to see the docktile instead of the real output */ - /* Clear background to transparent */ - glClear(GL_COLOR_BUFFER_BIT); - - /* Blit the content of the FBO to the screen. */ - for (i = 0; i < m_cClipRects; ++i) + else { - GLint x1 = m_paClipRects[4*i]; - GLint y1 = r.size.height - m_paClipRects[4*i+1]; - GLint x2 = m_paClipRects[4*i+2]; - GLint y2 = r.size.height - m_paClipRects[4*i+3]; - glBlitFramebufferEXT(x1, y1 + m_RootShift.y, x2, y2 + m_RootShift.y, - x1 - m_RootShift.x, y1, x2 - m_RootShift.x, y2, - GL_COLOR_BUFFER_BIT, GL_NEAREST); + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); } + } + else + { + Assert(0); + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + } + } #endif /* glFinish(); */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); [m_pSharedGLCtx flushBuffer]; +} - [m_pGLCtx makeCurrentContext]; - /* Reset to previous buffer bindings. */ - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, oldReadFBOId); - glReadBuffer(oldReadId); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, oldDrawFBOId); - glDrawBuffer(oldDrawId); - } - } -#else - [m_pGLCtx flushBuffer]; -#endif +- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY*)pChangedEntry +{ + [self vboxTryDraw]; +} + +- (void)vboxBlitterSyncWindow +{ + CR_BLITTER_WINDOW WinInfo; + NSRect r; + + if (!m_pBlitter) + return; + + memset(&WinInfo, 0, sizeof (WinInfo)); + + r = [self frame]; + WinInfo.width = r.size.width; + WinInfo.height = r.size.height; + + Assert(WinInfo.width == m_RootRect.size.width); + Assert(WinInfo.height == m_RootRect.size.height); + + /*CrBltMuralSetCurrentInfo(m_pBlitter, NULL);*/ + + CrBltMuralSetCurrentInfo(m_pBlitter, &WinInfo); + CrBltCheckUpdateViewport(m_pBlitter); } -- (void)renderFBOToDockTile +#ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +static int g_cVBoxTgaCtr = 0; +#endif +- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor { NSRect r = [self frame]; NSRect rr = NSZeroRect; GLint i = 0; NSDockTile *pDT = nil; + float xStretch, yStretch; -#ifdef FBO - if ( m_FBOThumbId - && m_FBOThumbTexId - && [m_DockTileView thumbBitmap] != nil) + if ([m_DockTileView thumbBitmap] != nil) { /* Only update after at least 200 ms, cause glReadPixels is * heavy performance wise. */ uint64_t uiNewTime = RTTimeMilliTS(); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + if (uiNewTime - m_uiDockUpdateTime > 200) { m_uiDockUpdateTime = uiNewTime; @@ -1516,22 +1463,73 @@ glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); #endif + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + /* Clear background to transparent */ glClear(GL_COLOR_BUFFER_BIT); rr = [m_DockTileView frame]; - - for (i = 0; i < m_cClipRects; ++i) + + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) { - GLint x1 = m_paClipRects[4*i]; - GLint y1 = r.size.height - m_paClipRects[4*i+1]; - GLint x2 = m_paClipRects[4*i+2]; - GLint y2 = r.size.height - m_paClipRects[4*i+3]; - - glBlitFramebufferEXT(x1, y1 + m_RootShift.y, x2, y2 + m_RootShift.y, - x1 * m_FBOThumbScaleX, y1 * m_FBOThumbScaleY, x2 * m_FBOThumbScaleX, y2 * m_FBOThumbScaleY, - GL_COLOR_BUFFER_BIT, GL_LINEAR); + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + uint32_t i; + int rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + for (i = 0; i < cRegions; ++i) + { + const RTRECT * pSrcRect = &paSrcRegions[i]; + const RTRECT * pDstRect = &paDstRegions[i]; + RTRECT SrcRect, DstRect, RestrictSrcRect, RestrictDstRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + VBoxRectScale(&DstRect, m_FBOThumbScaleX, m_FBOThumbScaleY); + + if (VBoxRectIsZero(&DstRect)) + continue; + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch, yStretch); + VBoxRectTranslate(&RestrictSrcRect, -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags); + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); + } + } + else + { + Assert(0); + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + } } + glFinish(); glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); @@ -1540,23 +1538,24 @@ * happens. We have to lock this access, in the case the dock * is updated currently. */ [m_DockTileView lock]; - glReadPixels(0, 0, rr.size.width, rr.size.height, + glReadPixels(0, m_RootRect.size.height - rr.size.height, rr.size.width, rr.size.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, [[m_DockTileView thumbBitmap] bitmapData]); [m_DockTileView unlock]; - - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_FBOId); - glReadBuffer(m_FBOAttFrontId); + +#ifdef VBOX_WITH_CRDUMPER_THUMBNAIL + ++g_cVBoxTgaCtr; + crDumpNamedTGAF((GLint)rr.size.width, (GLint)rr.size.height, + [[m_DockTileView thumbBitmap] bitmapData], "/Users/leo/vboxdumps/dump%d.tga", g_cVBoxTgaCtr); +#endif pDT = [[NSApplication sharedApplication] dockTile]; /* Send a display message to the dock tile in the main thread */ [[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil waitUntilDone:NO]; - } } -#endif } - (void)clearVisibleRegions @@ -1569,7 +1568,7 @@ m_cClipRects = 0; } -- (void)setVisibleRegions:(GLint)cRects paRects:(GLint*)paRects +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint*)paRects { GLint cOldRects = m_cClipRects; @@ -1589,8 +1588,12 @@ m_cClipRects = cRects; memcpy(m_paClipRects, paRects, sizeof(GLint) * 4 * cRects); } - else if (cOldRects) - [self tryDraw]; + + /* we need to redwar on regions change, however the compositor now is cleared + * because all compositor&window data-related modifications are performed with compositor cleared + * the renderspu client will re-set the compositor after modifications are complete + * this way we indicate renderspu generic code not to ignore the empty compositor */ + m_pWinInfo->fCompositorPresentEmpty = GL_TRUE; } - (NSView*)dockTileScreen @@ -1615,11 +1618,12 @@ if (pView != nil) { NSRect dockFrame = [pView frame]; + /* todo: this is not correct, we should use framebuffer size here, while parent view frame size may differ in case of scrolling */ NSRect parentFrame = [m_pParentView frame]; m_FBOThumbScaleX = (float)dockFrame.size.width / parentFrame.size.width; m_FBOThumbScaleY = (float)dockFrame.size.height / parentFrame.size.height; - newFrame = NSMakeRect((int)(m_Pos.x * m_FBOThumbScaleX), (int)(dockFrame.size.height - (m_Pos.y + m_Size.height - m_RootShift.y) * m_FBOThumbScaleY), (int)(m_Size.width * m_FBOThumbScaleX), (int)(m_Size.height * m_FBOThumbScaleY)); + newFrame = NSMakeRect((int)(m_Pos.x * m_FBOThumbScaleX), (int)(dockFrame.size.height - (m_Pos.y + m_Size.height - m_yInvRootOffset) * m_FBOThumbScaleY), (int)(m_Size.width * m_FBOThumbScaleX), (int)(m_Size.height * m_FBOThumbScaleY)); /* NSRect newFrame = NSMakeRect ((int)roundf(m_Pos.x * m_FBOThumbScaleX), (int)roundf(dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (int)roundf(m_Size.width * m_FBOThumbScaleX), (int)roundf(m_Size.height * m_FBOThumbScaleY)); NSRect newFrame = NSMakeRect ((m_Pos.x * m_FBOThumbScaleX), (dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (m_Size.width * m_FBOThumbScaleX), (m_Size.height * m_FBOThumbScaleY)); @@ -1727,9 +1731,8 @@ void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx) { NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - /* [pCtx release]; - */ + /*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ [pPool release]; } @@ -1739,12 +1742,12 @@ void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx) * View management * ********************************************************************************/ -void cocoaViewCreate(NativeNSViewRef *ppView, NativeNSViewRef pParentView, GLbitfield fVisParams) +void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) { NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; /* Create our worker view */ - OverlayView* pView = [[OverlayView alloc] initWithFrame:NSZeroRect thread:RTThreadSelf() parentView:pParentView]; + OverlayView* pView = [[OverlayView alloc] initWithFrame:NSZeroRect thread:RTThreadSelf() parentView:pParentView winInfo:pWinInfo]; if (pView) { @@ -1778,7 +1781,8 @@ void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView) if (pParentView != nil) { [[pParentView window] addChildWindow:[pOView overlayWin] ordered:NSWindowAbove]; - [pOView createFBO]; + if ([pOView isEverSized]) + [pOView performSelectorOnMainThread:@selector(vboxReshapeOnReparentPerform) withObject:nil waitUntilDone:NO]; } } @@ -1886,162 +1890,58 @@ void cocoaViewGetGeometry(NativeNSViewRef pView, int *pX, int *pY, int *pW, int [pPool release]; } -void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) -{ - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - - DEBUG_MSG(("cocoaViewMakeCurrentContext(%p, %p)\n", (void*)pView, (void*)pCtx)); - - [(OverlayView*)pView setGLCtx:pCtx]; - [(OverlayView*)pView makeCurrentFBO]; - - [pPool release]; -} - -void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, GLint* paRects) +void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry) { NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - - [(OverlayView*)pView setVisibleRegions:cRects paRects:paRects]; - - [pPool release]; -} - -/******************************************************************************** -* -* Additional OpenGL wrapper -* -********************************************************************************/ -static void performSelectorOnView(SEL selector) -{ - NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; - - if (pCtx) - { - NSView *pView = [pCtx view]; - if (pView) - { - if ([pView respondsToSelector:selector]) - [pView performSelector:selector]; - } - } -} - -static void performSelectorOnViewOneArg(SEL selector, id arg1) -{ - NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; - - if (pCtx) - { - NSView *pView = [pCtx view]; - if (pView) - { - if ([pView respondsToSelector:selector]) - [pView performSelector:selector withObject:arg1]; - } - } -} - -static void performSelectorOnViewTwoArgs(SEL selector, id arg1, id arg2) -{ - NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; - - if (pCtx) + NSOpenGLContext *pCtx; + + /* view should not necesserily have a context set */ + pCtx = [(OverlayView*)pView glCtx]; + if (!pCtx) { - NSView *pView = [pCtx view]; - if (pView) + ContextInfo * pCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pCtxInfo) { - if ([pView respondsToSelector:selector]) - [pView performSelector:selector withObject:arg1 withObject:arg2]; + DEBUG_WARN(("renderspuDefaultSharedContextAcquire returned NULL")); + + [pPool release]; + return; } + + pCtx = pCtxInfo->context; + + [(OverlayView*)pView setGLCtx:pCtx]; } -} - -void cocoaFlush(void) -{ - NSOpenGLContext *pCtx = nil; - - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - - DEBUG_MSG_1(("glFlush called\n")); - - performSelectorOnView(@selector(flushFBO)); - - [pPool release]; -} - -void cocoaFinish(void) -{ - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - - DEBUG_MSG_1(("glFinish called\n")); - - performSelectorOnView(@selector(finishFBO)); - - [pPool release]; -} - -void cocoaBindFramebufferEXT(GLenum target, GLuint framebuffer) -{ - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - - DEBUG_MSG_1(("glBindFramebufferEXT called target: %d fb: %d\n", target, framebuffer)); - - performSelectorOnViewTwoArgs(@selector(bindFBO:withFrameBuffer:), (id)target, (id)framebuffer); - - [pPool release]; -} - -void cocoaCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) -{ - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - GLbitfield mask = GL_COLOR_BUFFER_BIT; - - DEBUG_MSG_1(("glCopyPixels called: %d,%d-%dx%d type: %d\n", x, y, width, height, type)); - -#ifdef FBO - if (type == GL_DEPTH) - mask = GL_DEPTH_BUFFER_BIT; - else if (type == GL_STENCIL) - mask = GL_STENCIL_BUFFER_BIT; - glBlitFramebufferEXT(x, y, x + width, y + height, x, y, x + width, y + height, mask, GL_NEAREST); -#else - glCopyPixels(x, y, width, height, type); -#endif - - [pPool release]; -} - -void cocoaGetIntegerv(GLenum pname, GLint *params) -{ - NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - -// DEBUG_MSG_1(("getIntergerv called: %d\n", pname)); - - performSelectorOnViewTwoArgs(@selector(stateInfo:withParams:), (id)pname, (id)params); + + [(OverlayView*)pView presentComposition:pChangedEntry]; [pPool release]; } -void cocoaReadBuffer(GLenum mode) +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) { NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - DEBUG_MSG_1(("glReadBuffer called: %d\n", mode)); + DEBUG_MSG(("cocoaViewMakeCurrentContext(%p, %p)\n", (void*)pView, (void*)pCtx)); - performSelectorOnViewOneArg(@selector(readBuffer:), (id)mode); + if (pView) + { + [(OverlayView*)pView setGLCtx:pCtx]; + [(OverlayView*)pView makeCurrentFBO]; + } + else + { + [NSOpenGLContext clearCurrentContext]; + } [pPool release]; } -void cocoaDrawBuffer(GLenum mode) +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint* paRects) { NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; - DEBUG_MSG_1(("glDrawBuffer called: %d\n", mode)); - - performSelectorOnViewOneArg(@selector(drawBuffer:), (id)mode); + [(OverlayView*)pView setVisibleRegions:cRects paRects:paRects]; [pPool release]; } - diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c index 734e7a1a..49d745a6 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c @@ -15,13 +15,13 @@ static void set_window_geometry( RenderSPU *render_spu, const char *response ) { - float x, y, w, h; + int x, y, w, h; CRASSERT(response[0] == '['); - sscanf( response, "[ %f, %f, %f, %f ]", &x, &y, &w, &h ); + sscanf( response, "[ %d, %d, %d, %d ]", &x, &y, &w, &h ); render_spu->defaultX = (int) x; render_spu->defaultY = (int) y; - render_spu->defaultWidth = (int) w; - render_spu->defaultHeight = (int) h; + render_spu->defaultWidth = (unsigned int) w; + render_spu->defaultHeight = (unsigned int) h; } static void set_default_visual( RenderSPU *render_spu, const char *response ) @@ -321,7 +321,6 @@ SPUOptions renderSPUOptions[] = { void renderspuSetVBoxConfiguration( RenderSPU *render_spu ) { - CRConnection *conn; int a; for (a=0; a<256; a++) diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c index eceb96f3..9bdcf293 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c @@ -85,6 +85,7 @@ renderDestroyWindow( Display *dpy, Window w ) return WindowExistsFlag; } +#if 0 /* * Garbage collection function. * Loop over all known windows and check if corresponding X window still @@ -109,6 +110,7 @@ renderspu_GCWindow(void) } } } +#endif static Colormap GetLUTColormap( Display *dpy, XVisualInfo *vi ) @@ -401,6 +403,314 @@ chooseFBConfig( Display *dpy, int screen, GLbitfield visAttribs ) } #endif /* GLX_VERSION_1_3 */ +static const char * renderspuGetDisplayName() +{ + const char *dpyName; + + if (render_spu.display_string[0]) + dpyName = render_spu.display_string; + else + { + crWarning("Render SPU: no display.."); + dpyName = NULL; + } + return dpyName; +} + +static int renderspuWinCmdWinCreate(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdWinDestroy(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!crHashtableAllocRegisterKey(render_spu.windowTable, CR_RENDER_WINCMD_ID)) + { + crError("CR_RENDER_WINCMD_ID %d is occupied already", CR_RENDER_WINCMD_ID); + return VERR_INVALID_STATE; + } + + render_spu.pWinToInfoTable = crAllocHashtable(); + if (render_spu.pWinToInfoTable) + { + dpyName = renderspuGetDisplayName(); + if (dpyName) + { + GLboolean bRc = renderspuInitVisual(&render_spu.WinCmdVisual, dpyName, render_spu.default_visual); + if (bRc) + { + bRc = renderspuWindowInitWithVisual(&render_spu.WinCmdWindow, &render_spu.WinCmdVisual, GL_FALSE, CR_RENDER_WINCMD_ID); + if (bRc) + { + XSelectInput(render_spu.WinCmdVisual.dpy, render_spu.WinCmdWindow.window, StructureNotifyMask); + render_spu.WinCmdAtom = XInternAtom(render_spu.WinCmdVisual.dpy, "VBoxWinCmd", False); + CRASSERT(render_spu.WinCmdAtom != None); + return VINF_SUCCESS; + } + else + { + crError("renderspuWindowInitWithVisual failed"); + } + /* there is no visual destroy impl currently + * @todo: implement */ + } + else + { + crError("renderspuInitVisual failed"); + } + } + else + { + crError("Render SPU: no display, aborting"); + } + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + render_spu.pWinToInfoTable = NULL; + } + else + { + crError("crAllocHashtable failed"); + } + return rc; +} + +static void renderspuWinCmdTerm() +{ + /* the window is not in the table, this will just ensure the key is freed */ + crHashtableDelete(render_spu.windowTable, CR_RENDER_WINCMD_ID, NULL); + renderspuWindowTerm(&render_spu.WinCmdWindow); + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + /* we do not have visual destroy functionality + * @todo implement */ +} + + +static bool renderspuWinCmdProcess(CR_RENDER_WINCMD* pWinCmd) +{ + bool fExit = false; + /* process commands */ + switch (pWinCmd->enmCmd) + { + case CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE: + crHashtableAdd(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, pWinCmd->pWindow); + XSelectInput(render_spu.WinCmdVisual.dpy, pWinCmd->pWindow->window, ExposureMask); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY: + crHashtableDelete(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, NULL); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_NOP: + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_EXIT: + renderspuWinCmdTerm(); + pWinCmd->rc = VINF_SUCCESS; + fExit = true; + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_CREATE: + pWinCmd->rc = renderspuWinCmdWinCreate(pWinCmd->pWindow); + break; + case CR_RENDER_WINCMD_TYPE_WIN_DESTROY: + pWinCmd->rc = renderspuWinCmdWinDestroy(pWinCmd->pWindow); + break; + default: + crError("unknown WinCmd command! %d", pWinCmd->enmCmd); + pWinCmd->rc = VERR_INVALID_PARAMETER; + break; + } + + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + return fExit; +} + +static DECLCALLBACK(int) renderspuWinCmdThreadProc(RTTHREAD ThreadSelf, void *pvUser) +{ + int rc; + bool fExit = false; + crDebug("RenderSPU: Window thread started (%x)", crThreadID()); + + rc = renderspuWinCmdInit(); + + /* notify the main cmd thread that we have started */ + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + + if (!RT_SUCCESS(rc)) + { + CRASSERT(!render_spu.pWinToInfoTable); + return rc; + } + + do + { + XEvent event; + XNextEvent(render_spu.WinCmdVisual.dpy, &event); + + switch (event.type) + { + case ClientMessage: + { + CRASSERT(event.xclient.window == render_spu.WinCmdWindow.window); + if (event.xclient.window == render_spu.WinCmdWindow.window) + { + if (render_spu.WinCmdAtom == event.xclient.message_type) + { + CR_RENDER_WINCMD *pWinCmd; + memcpy(&pWinCmd, event.xclient.data.b, sizeof (pWinCmd)); + fExit = renderspuWinCmdProcess(pWinCmd); + } + } + + break; + } + case Expose: + { + if (!event.xexpose.count) + { + WindowInfo *pWindow = (WindowInfo*)crHashtableSearch(render_spu.pWinToInfoTable, event.xexpose.window); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 0); + renderspuVBoxCompositorRelease(pWindow); + } + } + } + break; + } + default: + break; + } + } while (!fExit); + + return 0; +} + +static int renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE enmCmd, WindowInfo *pWindow) +{ + Status status; + XEvent event; + CR_RENDER_WINCMD WinCmd, *pWinCmd; + int rc; + + pWinCmd = &WinCmd; + pWinCmd->enmCmd = enmCmd; + pWinCmd->rc = VERR_GENERAL_FAILURE; + pWinCmd->pWindow = pWindow; + + memset(&event, 0, sizeof (event)); + event.type = ClientMessage; + event.xclient.window = render_spu.WinCmdWindow.window; + event.xclient.message_type = render_spu.WinCmdAtom; + event.xclient.format = 8; + memcpy(event.xclient.data.b, &pWinCmd, sizeof (pWinCmd)); + + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, StructureNotifyMask, &event); + if (!status) + { + Assert(0); + crWarning("XSendEvent returned null"); + return VERR_GENERAL_FAILURE; + } + + XFlush(render_spu.pCommunicationDisplay); + rc = RTSemEventWaitNoResume(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventWaitNoResume failed rc %d", rc); + return rc; + } + return pWinCmd->rc; +} + +int renderspu_SystemInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!render_spu.use_glxchoosevisual) { + /* sometimes want to set this option with ATI drivers */ + render_spu.ws.glXChooseVisual = NULL; + } + + /* setup communication display connection */ + dpyName = renderspuGetDisplayName(); + if (!dpyName) + { + crWarning("no display name, aborting"); + return VERR_GENERAL_FAILURE; + } + + render_spu.pCommunicationDisplay = XOpenDisplay(dpyName); + if (!render_spu.pCommunicationDisplay) + { + crWarning( "Couldn't open X display named '%s'", dpyName ); + return VERR_GENERAL_FAILURE; + } + + if ( !render_spu.ws.glXQueryExtension( render_spu.pCommunicationDisplay, NULL, NULL ) ) + { + crWarning( "Render SPU: Display %s doesn't support GLX", dpyName ); + return VERR_GENERAL_FAILURE; + } + + rc = RTSemEventCreate(&render_spu.hWinCmdCompleteEvent); + if (RT_SUCCESS(rc)) + { + rc = RTThreadCreate(&render_spu.hWinCmdThread, renderspuWinCmdThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxCrWinCmd"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + return VINF_SUCCESS; + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + } + else + { + crWarning("RTThreadCreate failed rc %d", rc); + } + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + + return rc; +} + +int renderspu_SystemTerm() +{ + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_EXIT, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("renderspuWinCmdSubmit EXIT failed rc %d", rc); + return rc; + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + return VINF_SUCCESS; +} GLboolean renderspu_SystemInitVisual( VisualInfo *visual ) @@ -418,20 +728,15 @@ renderspu_SystemInitVisual( VisualInfo *visual ) } #endif - if (render_spu.display_string[0]) - dpyName = render_spu.display_string; - else if (visual->displayName[0]) - dpyName = visual->displayName; - else - dpyName = NULL; - + dpyName = renderspuGetDisplayName(); if (!dpyName) { crWarning("Render SPU: no display, aborting"); return GL_FALSE; } - crDebug("Render SPU: Opening display %s", dpyName); + + crInfo("Render SPU: Opening display %s", dpyName); if (dpyName && (crStrncmp(dpyName, "localhost:11", 12) == 0 || @@ -640,15 +945,15 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) window->x = 0; window->y = 0; - window->width = xwa.width; - window->height = xwa.height; + window->BltInfo.width = xwa.width; + window->BltInfo.height = xwa.height; } /* i've changed default window size to be 0,0 but X doesn't like it */ - /*CRASSERT(window->width >= 1); - CRASSERT(window->height >= 1);*/ - if (window->width < 1) window->width = 1; - if (window->height < 1) window->height = 1; + /*CRASSERT(window->BltInfo.width >= 1); + CRASSERT(window->BltInfo.height >= 1);*/ + if (window->BltInfo.width < 1) window->BltInfo.width = 1; + if (window->BltInfo.height < 1) window->BltInfo.height = 1; /* * Get a colormap. @@ -707,7 +1012,7 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) crDebug("Render SPU: VBox parent window_id is: %x", render_spu_parent_window_id); window->window = XCreateWindow(dpy, render_spu_parent_window_id, window->x, window->y, - window->width, window->height, + window->BltInfo.width, window->BltInfo.height, 0, visual->visual->depth, InputOutput, visual->visual->visual, flags, &swa); } @@ -720,7 +1025,7 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) crDebug("Render SPU: Creating global window, parent: %x", RootWindow(dpy, visual->visual->screen)); window->window = XCreateWindow(dpy, RootWindow(dpy, visual->visual->screen), window->x, window->y, - window->width, window->height, + window->BltInfo.width, window->BltInfo.height, 0, visual->visual->depth, InputOutput, visual->visual->visual, flags, &swa); } @@ -790,8 +1095,8 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) hints.x = window->x; hints.y = window->y; - hints.width = window->width; - hints.height = window->height; + hints.width = window->BltInfo.width; + hints.height = window->BltInfo.height; hints.min_width = hints.width; hints.min_height = hints.height; hints.max_width = hints.width; @@ -825,7 +1130,6 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) XIfEvent( dpy, &event, WaitForMapNotify, (char *) window->window ); } - window->visible = showIt; if ((window->visual->visAttribs & CR_DOUBLE_BIT) && render_spu.nvSwapGroup) { /* NOTE: @@ -835,7 +1139,7 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) * app window is in a separate swap group while all the back-end windows * which form a mural are in the same swap group. */ - GLuint group = 0; /*render_spu.nvSwapGroup + window->id;*/ + GLuint group = 0; /*render_spu.nvSwapGroup + window->BltInfo.Base.id;*/ GLuint barrier = 0; JoinSwapGroup(dpy, visual->visual->screen, window->window, group, barrier); } @@ -844,9 +1148,15 @@ createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) * End GLX code */ crDebug( "Render SPU: actual window x, y, width, height: %d, %d, %d, %d", - window->x, window->y, window->width, window->height ); + window->x, window->y, window->BltInfo.width, window->BltInfo.height ); XSync(dpy, 0); + + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, window); + AssertRC(rc); + } return GL_TRUE; } @@ -860,15 +1170,15 @@ createPBuffer( VisualInfo *visual, WindowInfo *window ) window->y = 0; window->nativeWindow = 0; - CRASSERT(window->width > 0); - CRASSERT(window->height > 0); + CRASSERT(window->BltInfo.width > 0); + CRASSERT(window->BltInfo.height > 0); #ifdef GLX_VERSION_1_3 { int attribs[100], i = 0, w, h; CRASSERT(visual->fbconfig); - w = window->width; - h = window->height; + w = window->BltInfo.width; + h = window->BltInfo.height; attribs[i++] = GLX_PRESERVED_CONTENTS; attribs[i++] = True; attribs[i++] = GLX_PBUFFER_WIDTH; @@ -896,8 +1206,8 @@ GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) { if (visual->visAttribs & CR_PBUFFER_BIT) { - window->width = render_spu.defaultWidth; - window->height = render_spu.defaultHeight; + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; return createPBuffer(visual, window); } else { @@ -938,6 +1248,11 @@ renderspu_SystemDestroyWindow( WindowInfo *window ) * window. I know...personal responsibility and all... */ if (!window->nativeWindow) { + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, window); + AssertRC(rc); + } XDestroyWindow(window->visual->dpy, window->window); XSync(window->visual->dpy, 0); } @@ -1000,7 +1315,7 @@ renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, Context if (visual->visual) crDebug("Render SPU: Created %s context (%d) on display %s for visAttribs 0x%x", is_direct ? "DIRECT" : "INDIRECT", - context->id, + context->BltInfo.Base.id, DisplayString(visual->dpy), visual->visAttribs); @@ -1152,18 +1467,18 @@ renderspu_SystemDestroyContext( ContextInfo *context ) static void check_buffer_size( WindowInfo *window ) { - if (window->width != window->in_buffer_width - || window->height != window->in_buffer_height + if (window->BltInfo.width != window->in_buffer_width + || window->BltInfo.height != window->in_buffer_height || ! window->buffer) { crFree(window->buffer); - window->buffer = crCalloc(window->width * window->height + window->buffer = crCalloc(window->BltInfo.width * window->BltInfo.height * 4 * sizeof (GLubyte)); - window->in_buffer_width = window->width; - window->in_buffer_height = window->height; + window->in_buffer_width = window->BltInfo.width; + window->in_buffer_height = window->BltInfo.height; - crDebug("Render SPU: dimensions changed to %d x %d", window->width, window->height); + crDebug("Render SPU: dimensions changed to %d x %d", window->BltInfo.width, window->BltInfo.height); } } #endif @@ -1176,7 +1491,6 @@ renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, Bool b; CRASSERT(render_spu.ws.glXMakeCurrent); - window->appWindow = nativeWindow; /*crDebug("%s nativeWindow=0x%x", __FUNCTION__, (int) nativeWindow);*/ @@ -1185,16 +1499,20 @@ renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, check_buffer_size(window); render_spu.OSMesaMakeCurrent( (OSMesaContext) context->context, window->buffer, GL_UNSIGNED_BYTE, - window->width, window->height); + window->BltInfo.width, window->BltInfo.height); return; } #endif + nativeWindow = 0; + if (window && context) { + window->appWindow = nativeWindow; + if (window->visual != context->visual) { crDebug("Render SPU: MakeCurrent visual mismatch (win(%d) bits:0x%x != ctx(%d) bits:0x%x); remaking window.", - window->id, window->visual->visAttribs, - context->id, context->visual->visAttribs); + window->BltInfo.Base.id, window->visual->visAttribs, + context->BltInfo.Base.id, context->visual->visAttribs); /* * XXX have to revisit this issue!!! * @@ -1273,7 +1591,7 @@ renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, if (vid != (int) context->visual->visual->visualid) { crWarning("Render SPU: Can't bind context %d to CRUT/native window " "0x%x because of different X visuals (0x%x != 0x%x)!", - context->id, (int) nativeWindow, + context->BltInfo.Base.id, (int) nativeWindow, vid, (int) context->visual->visual->visualid); crWarning("Render SPU: Trying to recreate GLX context to match."); /* Try to recreate the GLX context so that it uses the same @@ -1327,7 +1645,7 @@ renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, crWarning("glXMakeCurrent(%p, 0x%x, %p) failed! (winId %d, ctxId %d)", window->visual->dpy, (int) window->window, (void *) context->context, - window->id, context->id ); + window->BltInfo.Base.id, context->BltInfo.Base.id ); } /*CRASSERT(b);*/ } @@ -1344,6 +1662,18 @@ renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, } #endif } + else + { + GET_CONTEXT(pCurCtx); + if (pCurCtx) + { + b = render_spu.ws.glXMakeCurrent( pCurCtx->currentWindow->visual->dpy, None, NULL); + if (!b) { + crWarning("glXMakeCurrent(%p, None, NULL) failed!", pCurCtx->currentWindow->visual->dpy); + } + } + + } #if 0 /* XXX disabled for now due to problem with threadtest.conf */ @@ -1360,8 +1690,8 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) { #ifdef USE_OSMESA if (render_spu.use_osmesa) { - window->width = w; - window->height = h; + window->BltInfo.width = w; + window->BltInfo.height = h; check_buffer_size(window); return; } @@ -1394,7 +1724,7 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) } } - if (window->width != w || window->height != h) { + if (window->BltInfo.width != w || window->BltInfo.height != h) { /* Only resize if the new dimensions really are different */ #ifdef CHROMIUM_THREADSAFE ContextInfo *currentContext = (ContextInfo *) crGetTSD(&_RenderTSD); @@ -1403,10 +1733,10 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) #endif /* Can't resize pbuffers, so destroy it and make a new one */ render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window); - window->width = w; - window->height = h; + window->BltInfo.width = w; + window->BltInfo.height = h; crDebug("Render SPU: Creating new %d x %d PBuffer (id=%d)", - w, h, window->id); + w, h, window->BltInfo.Base.id); if (!createPBuffer(window->visual, window)) { crWarning("Render SPU: Unable to create PBuffer (out of VRAM?)!"); } @@ -1419,6 +1749,15 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) } } else { + if (!w || !h) + { + /* X can not handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_FALSE ); + } + return; + } /* Resize ordinary X window */ /* * This is ugly, but it seems to be the only thing that works. @@ -1432,6 +1771,16 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) crDebug("Render SPU: XResizeWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, w, h); XResizeWindow(window->visual->dpy, window->window, w, h); XSync(window->visual->dpy, 0); + + if (!window->BltInfo.width || !window->BltInfo.height) + { + /* we have hidden the window instead of sizing it to (0;0) since X is unable to handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_TRUE ); + return; + } + } #if 0 for (attempt = 0; attempt < 3; attempt++) { /* try three times max */ XWindowAttributes attribs; @@ -1444,10 +1793,6 @@ renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) } #endif } - - /* finally, save the new size */ - window->width = w; - window->height = h; } @@ -1457,8 +1802,8 @@ renderspu_SystemGetWindowGeometry( WindowInfo *window, { #ifdef USE_OSMESA if (render_spu.use_osmesa) { - *w = window->width; - *h = window->height; + *w = window->BltInfo.width; + *h = window->BltInfo.height; return; } #endif @@ -1470,8 +1815,8 @@ renderspu_SystemGetWindowGeometry( WindowInfo *window, { *x = 0; *y = 0; - *w = window->width; - *h = window->height; + *w = window->BltInfo.width; + *h = window->BltInfo.height; } else { @@ -1544,7 +1889,7 @@ renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) } void -renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, GLint *pRects ) +renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, const GLint *pRects ) { #ifdef USE_OSMESA if (render_spu.use_osmesa) @@ -1609,18 +1954,64 @@ renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) { if (showIt) { - XMapWindow( window->visual->dpy, window->window ); - XSync(window->visual->dpy, 0); + if (window->BltInfo.width && window->BltInfo.height) + { + XMapWindow( window->visual->dpy, window->window ); + XSync(window->visual->dpy, 0); + } } else { XUnmapWindow( window->visual->dpy, window->window ); XSync(window->visual->dpy, 0); } - window->visible = showIt; } } +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* the CR_RENDER_FORCE_PRESENT_MAIN_THREAD is actually inherited from cocoa backend impl, + * here it forces rendering in WinCmd thread rather than a Main thread. + * it is used for debugging only in any way actually. + * @todo: change to some more generic macro name */ +#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw her eif we are really able to do that w/o bllocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0); + renderspuVBoxCompositorRelease(window); + } + else if (rc == VERR_SEM_BUSY) +#endif + { + Status status; + XEvent event; + render_spu.self.Flush(); + renderspuVBoxPresentBlitterEnsureCreated(window, 0); + + crMemset(&event, 0, sizeof (event)); + event.type = Expose; + event.xexpose.window = window->window; + event.xexpose.width = window->BltInfo.width; + event.xexpose.height = window->BltInfo.height; + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, 0, &event); + if (!status) + { + Assert(0); + crWarning("XSendEvent returned null"); + } + XFlush(render_spu.pCommunicationDisplay); + } +#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD + else + { + /* this is somewhat we do not expect */ + crWarning("renderspuVBoxCompositorTryAcquire failed rc %d", rc); + } +#endif +} static void MarkWindow(WindowInfo *w) @@ -1632,7 +2023,7 @@ MarkWindow(WindowInfo *w) gcValues.function = GXnoop; gc = XCreateGC(w->visual->dpy, w->nativeWindow, GCFunction, &gcValues); } - XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->width, w->height); + XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->BltInfo.width, w->BltInfo.height); } @@ -1683,3 +2074,13 @@ void renderspu_SystemReparentWindow(WindowInfo *window) XReparentWindow(window->visual->dpy, window->window, parent, window->x, window->y); XSync(window->visual->dpy, False); } + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c index d22af98f..6d46faf8 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c @@ -9,6 +9,7 @@ #include "cr_error.h" #include "cr_string.h" #include "cr_url.h" +#include "cr_environment.h" #include "renderspu.h" #include <stdio.h> @@ -94,16 +95,17 @@ static DWORD WINAPI renderSPUWindowThreadProc(void* unused) if (msg.message == WM_VBOX_RENDERSPU_CREATE_WINDOW) { LPCREATESTRUCT pCS = (LPCREATESTRUCT) msg.lParam; - HWND *phWnd; + HWND hWnd; + WindowInfo *pWindow = (WindowInfo *)pCS->lpCreateParams; CRASSERT(msg.lParam && !msg.wParam && pCS->lpCreateParams); - phWnd = pCS->lpCreateParams; - - *phWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style, + hWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style, pCS->x, pCS->y, pCS->cx, pCS->cy, pCS->hwndParent, pCS->hMenu, pCS->hInstance, &render_spu); + pWindow->hWnd = hWnd; + SetEvent(render_spu.hWinThreadReadyEvent); } else if (msg.message == WM_VBOX_RENDERSPU_DESTROY_WINDOW) @@ -138,6 +140,8 @@ renderSPUInit( int id, SPU *child, SPU *self, int numFuncs, numSpecial; GLint defaultWin, defaultCtx; WindowInfo *windowInfo; + const char * pcpwSetting; + int rc; (void) child; (void) context_id; @@ -188,20 +192,39 @@ renderSPUInit( int id, SPU *child, SPU *self, numFuncs += numSpecial; -#ifdef GLX - if (!render_spu.use_glxchoosevisual) { - /* sometimes want to set this option with ATI drivers */ - render_spu.ws.glXChooseVisual = NULL; + render_spu.contextTable = crAllocHashtableEx(1, INT32_MAX); + render_spu.windowTable = crAllocHashtableEx(1, INT32_MAX); + + render_spu.dummyWindowTable = crAllocHashtable(); + + pcpwSetting = crGetenv("CR_RENDER_ENABLE_SINGLE_PRESENT_CONTEXT"); + if (pcpwSetting) + { + if (pcpwSetting[0] == '0') + pcpwSetting = NULL; } -#endif - render_spu.window_id = 0; - render_spu.context_id = 0; - render_spu.contextTable = crAllocHashtable(); - render_spu.windowTable = crAllocHashtable(); + if (pcpwSetting) + { + /* TODO: need proper blitter synchronization, do not use so far! + * the problem is that rendering can be done in multiple thread: the main command (hgcm) thread and the redraw thread + * we currently use per-window synchronization, while we'll need a per-blitter synchronization if one blitter is used for multiple windows + * this is not done currently */ + crWarning("TODO: need proper blitter synchronization, do not use so far!"); + render_spu.blitterTable = crAllocHashtable(); + CRASSERT(render_spu.blitterTable); + } + else + render_spu.blitterTable = NULL; CRASSERT(render_spu.default_visual & CR_RGB_BIT); - + + rc = renderspu_SystemInit(); + if (!RT_SUCCESS(rc)) + { + crError("renderspu_SystemInit failed rc %d", rc); + return NULL; + } #ifdef USE_OSMESA if (render_spu.use_osmesa) { if (!crLoadOSMesa(&render_spu.OSMesaCreateContext, @@ -249,8 +272,8 @@ renderSPUInit( int id, SPU *child, SPU *self, */ crDebug("Render SPU: Creating default window (visBits=0x%x, id=0)", render_spu.default_visual); - defaultWin = renderspuWindowCreate( NULL, render_spu.default_visual ); - if (defaultWin != 0) { + defaultWin = renderspuWindowCreateEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_WINDOW_ID ); + if (defaultWin != CR_RENDER_DEFAULT_WINDOW_ID) { crError("Render SPU: Couldn't get a double-buffered, RGB visual with Z!"); return NULL; } @@ -258,13 +281,16 @@ renderSPUInit( int id, SPU *child, SPU *self, crDebug("Render SPU: Creating default context, visBits=0x%x", render_spu.default_visual ); - defaultCtx = renderspuCreateContext( NULL, render_spu.default_visual, 0 ); - CRASSERT(defaultCtx == 0); + defaultCtx = renderspuCreateContextEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_CONTEXT_ID, 0 ); + if (defaultCtx != CR_RENDER_DEFAULT_CONTEXT_ID) { + crError("Render SPU: failed to create default context!"); + return NULL; + } renderspuMakeCurrent( defaultWin, 0, defaultCtx ); /* Get windowInfo for the default window */ - windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, 0); + windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); CRASSERT(windowInfo); windowInfo->mapPending = GL_TRUE; @@ -347,17 +373,21 @@ renderSPUInit( int id, SPU *child, SPU *self, render_spu.gather_conns = NULL; + numFuncs = renderspu_SystemPostprocessFunctions(_cr_render_table, numFuncs, RT_ELEMENTS(_cr_render_table)); + crDebug("Render SPU: ---------- End of Init -------------"); return &render_functions; } - static void renderSPUSelfDispatch(SPUDispatchTable *self) { crSPUInitDispatchTable( &(render_spu.self) ); crSPUCopyDispatchTable( &(render_spu.self), self ); + crSPUInitDispatchTable( &(render_spu.blitterDispatch) ); + crSPUCopyDispatchTable( &(render_spu.blitterDispatch), self ); + render_spu.server = (CRServer *)(self->server); { @@ -377,8 +407,7 @@ static void renderSPUSelfDispatch(SPUDispatchTable *self) static void DeleteContextCallback( void *data ) { ContextInfo *context = (ContextInfo *) data; - renderspu_SystemDestroyContext(context); - crFree(context); + renderspuContextMarkDeletedAndRelease(context); } static void DeleteWindowCallback( void *data ) @@ -388,12 +417,45 @@ static void DeleteWindowCallback( void *data ) crFree(window); } +static void DeleteBlitterCallback( void *data ) +{ + PCR_BLITTER pBlitter = (PCR_BLITTER) data; + CrBltTerm(pBlitter); + crFree(pBlitter); +} + +static void renderspuBlitterCleanupCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + CRASSERT(window); + + renderspuVBoxPresentBlitterCleanup( window ); +} + static int renderSPUCleanup(void) { + renderspuVBoxCompositorClearAll(); + + if (render_spu.blitterTable) + { + crFreeHashtable(render_spu.blitterTable, DeleteBlitterCallback); + render_spu.blitterTable = NULL; + } + else + { + crHashtableWalk(render_spu.windowTable, renderspuBlitterCleanupCB, NULL); + + crHashtableWalk(render_spu.dummyWindowTable, renderspuBlitterCleanupCB, NULL); + } + + renderspuSetDefaultSharedContext(NULL); + crFreeHashtable(render_spu.contextTable, DeleteContextCallback); render_spu.contextTable = NULL; crFreeHashtable(render_spu.windowTable, DeleteWindowCallback); render_spu.windowTable = NULL; + crFreeHashtable(render_spu.dummyWindowTable, DeleteWindowCallback); + render_spu.dummyWindowTable = NULL; crFreeHashtable(render_spu.barrierHash, crFree); render_spu.barrierHash = NULL; @@ -482,26 +544,3 @@ DECLEXPORT(void) renderspuSetWindowId(uint64_t winId) { render_spu_parent_window_id = winId; } - -static void renderspuWindowVisibleRegionCB(unsigned long key, void *data1, void *data2) -{ - WindowInfo *window = (WindowInfo *) data1; - CRASSERT(window); - - renderspu_SystemWindowApplyVisibleRegion(window); -} - -DECLEXPORT(void) renderspuSetRootVisibleRegion(GLint cRects, GLint *pRects) -{ -#ifdef RT_OS_DARWIN - renderspu_SystemSetRootVisibleRegion(cRects, pRects); - - crHashtableWalk(render_spu.windowTable, renderspuWindowVisibleRegionCB, NULL); -#endif -} - -#ifndef RT_OS_DARWIN -void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) -{ -} -#endif diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c index c1a903f0..1bd745a8 100644 --- a/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c @@ -425,7 +425,49 @@ MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) /* int w,h; */ switch ( uMsg ) { + case WM_PAINT: + { + WindowInfo *pWindow = (WindowInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + HDC hDC; + PAINTSTRUCT Paint; + + Assert(pWindow->device_context); + hDC = BeginPaint(pWindow->hWnd, &Paint); + if (hDC) + { + BOOL bRc; + pWindow->redraw_device_context = hDC; + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 1); + + bRc = EndPaint(pWindow->hWnd, &Paint); + + pWindow->redraw_device_context = NULL; + + renderspuVBoxCompositorRelease(pWindow); + + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("EndPaint failed, winEr %d", winEr); + } + } + else + { + DWORD winEr = GetLastError(); + crWarning("BeginPaint failed, winEr %d", winEr); + } + } + } + break; + } case WM_SIZE: /* w = LOWORD( lParam ); * h = HIWORD( lParam ); */ @@ -557,7 +599,11 @@ bSetupPixelFormatEXT( HDC hdc, GLbitfield visAttribs) crDebug("Render SPU: wglChoosePixelFormatEXT (vis 0x%x, LastError 0x%x, pixelFormat 0x%x", vis, GetLastError(), pixelFormat); +#ifdef VBOX_CR_SERVER_FORCE_WGL render_spu.ws.wglSetPixelFormat( hdc, pixelFormat, &ppfd ); +#else + SetPixelFormat( hdc, pixelFormat, &ppfd ); +#endif crDebug("Render SPU: wglSetPixelFormat (Last error 0x%x)", GetLastError()); @@ -616,6 +662,7 @@ bSetupPixelFormatNormal( HDC hdc, GLbitfield visAttribs ) * by our faker library, otherwise we have to call the GDI * versions. */ +#ifdef VBOX_CR_SERVER_FORCE_WGL if (crGetenv( "CR_WGL_DO_NOT_USE_GDI" ) != NULL) { pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd ); @@ -633,6 +680,7 @@ bSetupPixelFormatNormal( HDC hdc, GLbitfield visAttribs ) render_spu.ws.wglDescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd ); } else +#endif { /* Okay, we were loaded manually. Call the GDI functions. */ pixelformat = ChoosePixelFormat( hdc, ppfd ); @@ -782,27 +830,27 @@ GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, Wi int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); - window->width = GetSystemMetrics( SM_CXSCREEN ) ; - window->height = GetSystemMetrics( SM_CYSCREEN ) ; + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; - crDebug( "Render SPU: Window Dims: %d, %d", window->width, window->height ); + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); window->x = render_spu->defaultX - smCxFixedFrame - 1; window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; - window_plus_caption_width = window->width + 2 * smCxFixedFrame; - window_plus_caption_height = window->height + 2 * smCyFixedFrame + smCyCaption; + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; #else /* Since it's undecorated, we don't have to do anything fancy * with these parameters. */ - window->width = GetSystemMetrics( SM_CXSCREEN ) ; - window->height = GetSystemMetrics( SM_CYSCREEN ) ; + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; window->x = 0; window->y = 0; - window_plus_caption_width = window->width; - window_plus_caption_height = window->height; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; #endif } @@ -819,8 +867,8 @@ GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, Wi smCyCaption = GetSystemMetrics( SM_CYCAPTION ); crDebug( "Render SPU: Got the Caption " ); - window_plus_caption_width = window->width + 2 * smCxFixedFrame; - window_plus_caption_height = window->height + 2 * smCyFixedFrame + smCyCaption; + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; window->x = render_spu.defaultX - smCxFixedFrame; window->y = render_spu.defaultY - smCyFixedFrame - smCyCaption; @@ -845,11 +893,11 @@ GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, Wi if (!showIt) { renderspu_SystemShowWindow( window, 0 ); - if (window->height <= 0 || window->width <= 0) + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) { renderspu_SystemWindowSize(window, - window->width > 0 ? window->width : 4, - window->height > 0 ? window->height : 4); + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); } } else @@ -878,6 +926,11 @@ GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, Wi ShowCursor( FALSE ); window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); @@ -998,27 +1051,27 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); - window->width = GetSystemMetrics( SM_CXSCREEN ) ; - window->height = GetSystemMetrics( SM_CYSCREEN ) ; + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; - crDebug( "Render SPU: Window Dims: %d, %d", window->width, window->height ); + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); window->x = render_spu->defaultX - smCxFixedFrame - 1; window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; - window_plus_caption_width = window->width + 2 * smCxFixedFrame; - window_plus_caption_height = window->height + 2 * smCyFixedFrame + smCyCaption; + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; #else /* Since it's undecorated, we don't have to do anything fancy * with these parameters. */ - window->width = GetSystemMetrics( SM_CXSCREEN ) ; - window->height = GetSystemMetrics( SM_CYSCREEN ) ; + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; window->x = 0; window->y = 0; - window_plus_caption_width = window->width; - window_plus_caption_height = window->height; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; #endif } @@ -1034,8 +1087,8 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt smCyCaption = GetSystemMetrics( SM_CYCAPTION ); crDebug( "Render SPU: Got the Caption " ); - window_plus_caption_width = window->width + 2 * smCxFixedFrame; - window_plus_caption_height = window->height + 2 * smCyFixedFrame + smCyCaption; + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; window->x = render_spu.defaultX; window->y = render_spu.defaultY; @@ -1046,13 +1099,13 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt WINDOW_NAME, WINDOW_NAME, window_style, window->x, window->y, - window->width, - window->height, + window->BltInfo.width, + window->BltInfo.height, (void*) render_spu_parent_window_id, NULL, hinstance, &render_spu );*/ { CREATESTRUCT cs; - cs.lpCreateParams = &window->hWnd; + cs.lpCreateParams = window; cs.dwExStyle = WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY; cs.lpszName = WINDOW_NAME; @@ -1060,8 +1113,8 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt cs.style = window_style; cs.x = window->x; cs.y = window->y; - cs.cx = window->width; - cs.cy = window->height; + cs.cx = window->BltInfo.width; + cs.cy = window->BltInfo.height; cs.hwndParent = (void*) render_spu_parent_window_id; cs.hMenu = NULL; cs.hInstance = hinstance; @@ -1110,16 +1163,20 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt if (!showIt) { renderspu_SystemShowWindow( window, 0 ); - if (window->height <= 0 || window->width <= 0) + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) { renderspu_SystemWindowSize(window, - window->width > 0 ? window->width : 4, - window->height > 0 ? window->height : 4); + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); } } else { +#ifdef DEBUG_misha + crWarning( "Render SPU: Showing the window" ); +#else crDebug( "Render SPU: Showing the window" ); +#endif crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd); } @@ -1127,7 +1184,7 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt /* Intel drivers require a window to be visible for proper 3D rendering, * so set it visible and handle the visibility with visible regions (see below) */ - if (window->id) + if (window->BltInfo.Base.id != CR_RENDER_DEFAULT_WINDOW_ID) { ShowWindow( window->hWnd, SW_SHOWNORMAL ); } @@ -1140,17 +1197,22 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt //SetForegroundWindow( visual->hWnd ); SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y, - window->width, window->height, + window->BltInfo.width, window->BltInfo.height, ( render_spu.fullscreen ? (SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOACTIVATE ) : SWP_NOACTIVATE ) ); crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, - window->x, window->y, window->width, window->height); + window->x, window->y, window->BltInfo.width, window->BltInfo.height); if ( render_spu.fullscreen ) ShowCursor( FALSE ); window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); @@ -1160,16 +1222,52 @@ GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt return GL_FALSE; } + /* set the window pointer data at the last step to ensure our WM_PAINT callback does not do anything until we are fully initialized */ + { + LONG_PTR oldVal = SetWindowLongPtr(window->hWnd, GWLP_USERDATA, (LONG_PTR)window); + DWORD winEr = GetLastError(); + Assert(!oldVal && winEr == NO_ERROR); + } + return GL_TRUE; } +static void renderspuWindowRgnApply(WindowInfo *window) +{ + HRGN hRgn = window->hRgn; + if (hRgn) + { + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that. + * this is why we need to make a copy of the regions to be passed in */ + + int result; + hRgn = CreateRectRgn(0, 0, 0, 0); + if (!hRgn) + { + WARN(("CreateRectRgn failed")); + return; + } + + result = CombineRgn(hRgn, window->hRgn, NULL, RGN_COPY); + if (result == ERROR) + { + WARN(("CombineRgn failed")); + return; + } + } + + SetWindowRgn(window->hWnd, hRgn, true); +} + /* Either show or hide the render SPU's window. */ void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) { if (showIt) { crDebug("SHOW renderspu_SystemShowWindow: %x", window->hWnd); - SetWindowRgn(window->hWnd, window->hRgn, true); + renderspuWindowRgnApply(window); } else { @@ -1177,11 +1275,44 @@ void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) crDebug("HIDE renderspu_SystemShowWindow: %x", window->hWnd); hRgn = CreateRectRgn(0, 0, 0, 0); SetWindowRgn(window->hWnd, hRgn, true); - DeleteObject(hRgn); + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that */ } window->visible = showIt; } +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* the CR_RENDER_FORCE_PRESENT_MAIN_THREAD is actually inherited from cocoa backend impl, + * here it forces rendering in WinCmd thread rather than a Main thread. + * it is used for debugging only in any way actually. + * @todo: change to some more generic macro name */ +#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw her eif we are really able to do that w/o bllocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0); + renderspuVBoxCompositorRelease(window); + } + else if (rc == VERR_SEM_BUSY) +#endif + { + render_spu.self.Flush(); + renderspuVBoxPresentBlitterEnsureCreated(window, 0); + RedrawWindow(window->hWnd, NULL, NULL, RDW_INTERNALPAINT); + } +#ifndef CR_RENDER_FORCE_PRESENT_MAIN_THREAD + else + { + /* this is somewhat we do not expect */ + crWarning("renderspuVBoxCompositorTryAcquire failed rc %d", rc); + } +#endif +} + GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ) { (void) sharedContext; @@ -1215,29 +1346,20 @@ void renderspu_SystemDestroyContext( ContextInfo *context ) static GLboolean renderspuChkActivateSharedContext(ContextInfo *sharedContext) { - GLint crWindow; WindowInfo *window; if (sharedContext->hRC) return GL_TRUE; - CRASSERT(sharedContext->id); + CRASSERT(sharedContext->BltInfo.Base.id); if (sharedContext->shared) renderspuChkActivateSharedContext(sharedContext->shared); - crWindow = renderspuWindowCreate(sharedContext->visual->displayName, sharedContext->visual->visAttribs); - if (!crWindow) - { - crError("renderspuChkActivateSharedContext: renderspuWindowCreate failed!"); - return GL_FALSE; - } - - window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow); + window = renderspuGetDummyWindow(sharedContext->visual->visAttribs); if (!window) { - crError("renderspuChkActivateSharedContext: crHashtableSearch failed!"); - renderspuWindowDestroy(crWindow); + crError("renderspuChkActivateSharedContext: renderspuGetDummyWindow failed!"); return GL_FALSE; } @@ -1249,12 +1371,9 @@ static GLboolean renderspuChkActivateSharedContext(ContextInfo *sharedContext) if (!sharedContext->hRC) { crError( "Render SPU: (renderspuChkActivateSharedContext) Couldn't create the context for the window (error 0x%x)", GetLastError() ); - renderspuWindowDestroy(crWindow); return GL_FALSE; } - sharedContext->currentWindow = window; - return GL_TRUE; } @@ -1274,12 +1393,12 @@ void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, Contex /*@todo Chromium has no correct code to remove window ids and associated info from * various tables. This is hack which just hides the root case. */ - crDebug("Recreating window in renderspu_SystemMakeCurrent\n"); + crWarning("Recreating window in renderspu_SystemMakeCurrent\n"); renderspu_SystemDestroyWindow( window ); renderspu_SystemVBoxCreateWindow( context->visual, window->visible, window ); } - if (render_spu.render_to_app_window && nativeWindow) + if (0/*render_spu.render_to_app_window && nativeWindow*/) { /* The render_to_app_window option * is set and we've got a nativeWindow @@ -1320,6 +1439,7 @@ void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, Contex else { if (!context->hRC) { + CRASSERT(!nativeWindow); if (context->shared) { /* first make sure we have shared context created */ @@ -1337,11 +1457,16 @@ void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, Contex && context->hRC) { /* share lists */ - render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + BOOL bRc = render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("wglShareLists failed, winEr %d", winEr); + } } /*Requery ext function pointers, we skip dummy ctx as it should never be used with ext functions*/ - if (0 && context->id) + if (0 && context->BltInfo.Base.id) { int numFuncs, i; SPUNamedFunctionTable ext_table[1000]; @@ -1366,7 +1491,7 @@ void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, Contex } /*crDebug("MakeCurrent 0x%x, 0x%x", window->device_context, context->hRC);*/ - if (!render_spu.ws.wglMakeCurrent(window->device_context, context->hRC)) + if (!render_spu.ws.wglMakeCurrent(!nativeWindow ? window->device_context : window->redraw_device_context, context->hRC)) { DWORD err = GetLastError(); crError("Render SPU: (MakeCurrent) failed to make 0x%x, 0x%x current with 0x%x error.", window->device_context, context->hRC, err); @@ -1400,8 +1525,8 @@ void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) crDebug("Render SPU: SetWindowSize (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h); } /* save the new size */ - window->width = w; - window->height = h; + window->BltInfo.width = w; + window->BltInfo.height = h; } @@ -1443,18 +1568,18 @@ void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) /*SetWindowRgn(window->visual->hWnd, NULL, false);*/ if (!SetWindowPos( window->hWnd, HWND_TOP, - x, y, window->width, window->height, winprop )) { - crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, x, y, window->width, window->height); + x, y, window->BltInfo.width, window->BltInfo.height, winprop )) { + crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, x, y, window->BltInfo.width, window->BltInfo.height); } else { crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, - x, y, window->width, window->height); + x, y, window->BltInfo.width, window->BltInfo.height); } /* save the new position */ window->x = x; window->y = y; } -void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, GLint* pRects) +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) { GLint i; HRGN hRgn, hTmpRgn; @@ -1477,12 +1602,12 @@ void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, GLint DeleteObject(hTmpRgn); } + window->hRgn = hRgn; + if (window->visible) - SetWindowRgn(window->hWnd, hRgn, true); + renderspuWindowRgnApply(window); crDebug("Render SPU: SetWindowRgn (%x, cRects=%i)", window->hWnd, cRects); - - window->hRgn = hRgn; } static void renderspuHandleWindowMessages( HWND hWnd ) @@ -1502,7 +1627,7 @@ void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) int return_value; /* peek at the windows message queue */ - renderspuHandleWindowMessages( w->hWnd ); +// renderspuHandleWindowMessages( w->hWnd ); /* render_to_app_window: * w->nativeWindow will only be non-zero if the @@ -1511,14 +1636,18 @@ void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) * structure. */ if (render_spu.render_to_app_window && w->nativeWindow) { +#ifdef VBOX_CR_SERVER_FORCE_WGL return_value = render_spu.ws.wglSwapBuffers( w->nativeWindow ); +#else + return_value = SwapBuffers( w->nativeWindow ); +#endif } else { /* HRGN hRgn1, hRgn2, hRgn3; HWND hWndParent; LONG ws; - hRgn1 = CreateRectRgn(0, 0, w->width, w->height); + hRgn1 = CreateRectRgn(0, 0, w->BltInfo.width, w->BltInfo.height); hRgn2 = CreateRectRgn(50, 50, 100, 100); hRgn3 = CreateRectRgn(0, 0, 0, 0); CombineRgn(hRgn3, hRgn1, hRgn2, RGN_DIFF); @@ -1554,7 +1683,11 @@ void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom); */ +#ifdef VBOX_CR_SERVER_FORCE_WGL return_value = render_spu.ws.wglSwapBuffers( w->device_context ); +#else + return_value = SwapBuffers( w->device_context ); +#endif } if (!return_value) { @@ -1570,3 +1703,23 @@ void renderspu_SystemReparentWindow(WindowInfo *window) { SetParent(window->hWnd, (HWND)render_spu_parent_window_id); } + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c index ca4d82b3..cdf0591b 100644 --- a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c index 5c79f909..daf2b839 100644 --- a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c @@ -74,3 +74,14 @@ void crUnpackExtendGetPixelMapusv(void) cr_unpackDispatch.GetPixelMapusv( map, values ); } + +void crUnpackExtendVBoxTexPresent(void) +{ + GLuint texture = READ_DATA( 8, GLuint ); + GLuint cfg = READ_DATA( 12, GLuint ); + GLint xPos = READ_DATA( 16, GLint ); + GLint yPos = READ_DATA( 20, GLint ); + GLint cRects = READ_DATA( 24, GLint ); + GLint *pRects = (GLint *)DATA_POINTER( 28, GLvoid ); + cr_unpackDispatch.VBoxTexPresent( texture, cfg, xPos, yPos, cRects, pRects ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c index 6b3204ce..2e90b3d5 100644 --- a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -39,8 +39,8 @@ void crUnpackExtendShaderSource(void) GLsizei count = READ_DATA(12, GLsizei); GLint hasNonLocalLen = READ_DATA(16, GLsizei); GLint *pLocalLength = DATA_POINTER(20, GLint); - const char **ppStrings = NULL; - GLsizei i; + char **ppStrings = NULL; + GLsizei i, j, jUpTo; int pos=20+count*sizeof(*pLocalLength); if (hasNonLocalLen>0) @@ -60,9 +60,24 @@ void crUnpackExtendShaderSource(void) { pLocalLength[i] -= 1; } + + Assert(pLocalLength[i] > 0); + jUpTo = i == count -1 ? pLocalLength[i] - 1 : pLocalLength[i]; + for (j = 0; j < jUpTo; ++j) + { + char *pString = ppStrings[i]; + + if (pString[j] == '\0') + { + Assert(j == jUpTo - 1); + pString[j] = '\n'; + } + } } - cr_unpackDispatch.ShaderSource(shader, count, ppStrings, length ? length : pLocalLength); +// cr_unpackDispatch.ShaderSource(shader, count, ppStrings, length ? length : pLocalLength); + cr_unpackDispatch.ShaderSource(shader, 1, ppStrings, 0); + crFree(ppStrings); } @@ -249,7 +264,7 @@ void crUnpackExtendGetAttachedShaders(void) void crUnpackExtendGetAttachedObjectsARB(void) { - GLhandleARB containerObj = READ_DATA(8, GLhandleARB); + VBoxGLhandleARB containerObj = READ_DATA(8, VBoxGLhandleARB); GLsizei maxCount = READ_DATA(12, GLsizei); SET_RETURN_PTR(16); SET_WRITEBACK_PTR(24); @@ -258,7 +273,7 @@ void crUnpackExtendGetAttachedObjectsARB(void) void crUnpackExtendGetInfoLogARB(void) { - GLhandleARB obj = READ_DATA(8, GLhandleARB); + VBoxGLhandleARB obj = READ_DATA(8, VBoxGLhandleARB); GLsizei maxLength = READ_DATA(12, GLsizei); SET_RETURN_PTR(16); SET_WRITEBACK_PTR(24); diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c index 4bae17d1..961f7ac0 100644 --- a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special index e0f9a743..cd1fc415 100644 --- a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special @@ -180,3 +180,4 @@ UniformMatrix2x4fv UniformMatrix4x2fv UniformMatrix3x4fv UniformMatrix4x3fv +VBoxTexPresent
\ No newline at end of file diff --git a/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c index 633c9400..d80395f2 100644 --- a/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c +++ b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/HostServices/testcase/tstHGCMSvc.cpp b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp index 6adf1a28..fcd39614 100644 --- a/src/VBox/HostServices/testcase/tstHGCMSvc.cpp +++ b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |
