Index: /trunk/src/VBox/GuestHost/OpenGL/include/cr_blitter.h
===================================================================
--- /trunk/src/VBox/GuestHost/OpenGL/include/cr_blitter.h	(revision 45347)
+++ /trunk/src/VBox/GuestHost/OpenGL/include/cr_blitter.h	(revision 45348)
@@ -116,4 +116,10 @@
 }
 
+DECLINLINE(GLint) CrBltGetVisBits(PCR_BLITTER pBlitter)
+{
+    return pBlitter->CtxInfo.Base.visualBits;
+}
+
+
 DECLINLINE(GLboolean) CrBltIsEverEntered(PCR_BLITTER pBlitter)
 {
Index: /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h
===================================================================
--- /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h	(revision 45347)
+++ /trunk/src/VBox/GuestHost/OpenGL/include/cr_server.h	(revision 45348)
@@ -22,4 +22,8 @@
 #include <iprt/err.h>
 #include <iprt/string.h>
+#include <iprt/list.h>
+#include <iprt/thread.h>
+#include <iprt/critsect.h>
+#include <iprt/semaphore.h>
 
 #include <VBox/vmm/ssm.h>
@@ -87,4 +91,114 @@
 } CRCreateInfo_t;
 
+
+/* VRAM->RAM worker thread */
+
+typedef enum
+{
+    CR_SERVER_RPW_STATE_UNINITIALIZED = 0,
+    CR_SERVER_RPW_STATE_INITIALIZING,
+    CR_SERVER_RPW_STATE_INITIALIZED,
+    CR_SERVER_RPW_STATE_UNINITIALIZING,
+} CR_SERVER_RPW_STATE;
+
+/* worker control command */
+typedef enum
+{
+    CR_SERVER_RPW_CTL_TYPE_UNDEFINED = 0,
+    CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE,
+    CR_SERVER_RPW_CTL_TYPE_TERM
+} CR_SERVER_RPW_CTL_TYPE;
+
+struct CR_SERVER_RPW_ENTRY;
+
+typedef struct CR_SERVER_RPW_CTL {
+    CR_SERVER_RPW_CTL_TYPE enmType;
+    int rc;
+    RTSEMEVENT hCompleteEvent;
+    /* valid for *_WAIT_COMPLETE and *_CANCEL */
+    struct CR_SERVER_RPW_ENTRY *pEntry;
+} CR_SERVER_RPW_CTL;
+
+
+struct CR_SERVER_RPW_ENTRY;
+
+typedef DECLCALLBACKPTR(void, PFNCR_SERVER_RPW_DATA) (const struct CR_SERVER_RPW_ENTRY* pEntry, void *pvEntryTexData);
+
+typedef struct CR_SERVER_RPW_ENTRY
+{
+    RTRECTSIZE Size;
+    /*  We have to use 4 textures here.
+     *
+     *  1. iDrawTex - the texture clients can draw to and then submit it for contents acquisition via crServerRpwEntrySubmit
+     *  2. iSubmittedTex - the texture submitted to the worker for processing and, whose processing has not start yet,
+     *     i.e. it is being in the queue and can be safely removed/replaced [from] there
+     *  3. iWorkerTex - the texture being prepared & passed by the worker to the GPU (stage 1 of a worker contents acquisition process)
+     *  4. iGpuTex - the texture passed/processed to/by the GPU, whose data is then acquired by the server (stage 2 of a worker contents acquisition process)
+     *
+     *  - There can be valid distinct iGpuTex, iWorkerTex, iSubmittedTex and iDrawTex present simultaneously.
+     *  - Either or both of iSubmittedTex and iFreeTex are always valid
+     *
+     *  Detail:
+     *
+     *  - iSubmittedTex and iFreeTex modifications are performed under CR_SERVER_RPW::CritSect lock.
+     *
+     *  - iDrawTex can only be changed by client side (i.e. the crServerRpwEntrySubmit caller), this is why client thread can access it w/o a lock
+     *  - iSubmittedTex and iFreeTex can be modified by both client and worker, so lock is always required
+     *
+     *  - iDrawTex can be accessed by client code only
+     *  - iWorkerTex and iGpuTex can be accessed by worker code only
+     *  - iSubmittedTex and iFreeTex can be accessed under CR_SERVER_RPW::CritSect lock only
+     *  - either or both of iSubmittedTex and iFreeTex are always valid (see below for more explanation),
+     *    this is why client can easily determine the new iDrawTex value on Submit, i.e. :
+     *
+     *          (if initial iSubmittedTex was valid)
+     *                ---------------
+     *                |              ^
+     *                >              |
+     *   Submit-> iDrawTex -> iSubmittedTex
+     *                ^
+     *                | (if initial iSubmittedTex was NOT valid)
+     *              iFreeTex
+     *
+     *  - The worker can invalidate the iSubmittedTex (i.e. do iSubmittedTex -> iWorkerTex) only after it is done
+     *    with the last iWorkerTex -> iGpuTex transformation freeing the previously used iGpuTex to iFreeTex.
+     *
+     *  - A simplified worker iXxxTex transformation logic is:
+     *    1. iFreeTex is initially valid
+     *    2. iSubmittedTex -> iWorkerTex;
+     *    3. submit iWorkerTex acquire request to the GPU
+     *    4. complete current iGpuTex
+     *    5. iGpuTex -> iFreeTex
+     *    6. iWorkerTex -> iGpuTex
+     *    7. goto 1
+     *
+     *  */
+    int8_t iTexDraw;
+    int8_t iTexSubmitted;
+    int8_t iTexWorker;
+    int8_t iTexGpu;
+    int8_t iCurPBO;
+    GLuint aidWorkerTexs[4];
+    GLuint aidPBOs[2];
+    RTLISTNODE WorkEntry;
+    RTLISTNODE WorkerWorkEntry;
+    RTLISTNODE GpuSubmittedEntry;
+    PFNCR_SERVER_RPW_DATA pfnData;
+} CR_SERVER_RPW_ENTRY;
+
+typedef struct CR_SERVER_RPW {
+    RTLISTNODE WorkList;
+    RTCRITSECT CritSect;
+    RTSEMEVENT hSubmitEvent;
+    /* only one outstanding command is supported,
+     * and ctl requests must be cynchronized, hold it right here */
+    CR_SERVER_RPW_CTL Ctl;
+    int ctxId;
+    GLint ctxVisBits;
+    RTTHREAD hThread;
+} CR_SERVER_RPW;
+/* */
+
+
 /**
  * Mural info
@@ -142,4 +256,6 @@
     VBOXVR_SCR_COMPOSITOR_ENTRY RootVrCEntry;
     VBOXVR_SCR_COMPOSITOR RootVrCompositor;
+
+    CR_SERVER_RPW_ENTRY RpwEntry;
 
     /* bitfield representing contexts the mural has been ever current with
@@ -269,4 +385,5 @@
 
 /* */
+
 /* BFB (BlitFramebuffer Blitter) flags
  * so far only CR_SERVER_BFB_ON_ALWAIS is supported and is alwais used if any flag is set */
@@ -336,4 +453,6 @@
     uint32_t fBlitterMode;
     CR_BLITTER Blitter;
+
+    CR_SERVER_RPW RpwWorker;
 
     /** configuration options */
@@ -395,4 +514,5 @@
                                                      *using callback above to update vbox framebuffers*/
     GLuint                fPresentModeDefault; /*can be set with CR_SERVER_DEFAULT_RENDER_TYPE*/
+    GLuint                fVramPresentModeDefault;
     GLboolean             bUsePBOForReadback;       /*Use PBO's for data readback*/
 
Index: /trunk/src/VBox/HostServices/SharedOpenGL/Makefile.kmk
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/Makefile.kmk	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/Makefile.kmk	(revision 45348)
@@ -135,4 +135,5 @@
 	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 \
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h	(revision 45348)
@@ -136,20 +136,21 @@
 #define CR_SERVER_REDIR_F_FBO           0x02
 /* used with CR_SERVER_REDIR_F_FBO only
- * makes a separate texture to be used for maintaining the window framebuffer presented data */
-#define CR_SERVER_REDIR_F_FBO_VMFB_TEX  0x04
-/* used with CR_SERVER_REDIR_F_FBO only
  * indicates that FBO data should be copied to RAM for further processing */
-#define CR_SERVER_REDIR_F_FBO_RAM       0x08
+#define CR_SERVER_REDIR_F_FBO_RAM       0x04
 /* used with CR_SERVER_REDIR_F_FBO_RAM only
  * indicates that FBO data should be passed to VRDP backend */
-#define CR_SERVER_REDIR_F_FBO_RAM_VRDP  0x10
+#define CR_SERVER_REDIR_F_FBO_RAM_VRDP  0x08
 /* used with CR_SERVER_REDIR_F_FBO_RAM only
  * indicates that FBO data should be passed to VM Framebuffer */
-#define CR_SERVER_REDIR_F_FBO_RAM_VMFB  0x20
+#define CR_SERVER_REDIR_F_FBO_RAM_VMFB  0x10
+/* used with CR_SERVER_REDIR_F_FBO_RAM only
+ * makes the RPW (Read Pixels Worker) mechanism to be used for GPU memory aquisition */
+#define CR_SERVER_REDIR_F_FBO_RPW       0x20
+
 
 #define CR_SERVER_REDIR_F_ALL           0x3f
 
 #define CR_SERVER_REDIR_FGROUP_REQUIRE_FBO     (CR_SERVER_REDIR_F_ALL & ~CR_SERVER_REDIR_F_DISPLAY)
-#define CR_SERVER_REDIR_FGROUP_REQUIRE_FBO_RAM (CR_SERVER_REDIR_F_FBO_RAM_VRDP | CR_SERVER_REDIR_F_FBO_RAM_VMFB)
+#define CR_SERVER_REDIR_FGROUP_REQUIRE_FBO_RAM (CR_SERVER_REDIR_F_FBO_RAM_VRDP | CR_SERVER_REDIR_F_FBO_RAM_VMFB | CR_SERVER_REDIR_F_FBO_RPW)
 
 DECLINLINE(GLuint) crServerRedirModeAdjust(GLuint value)
@@ -168,5 +169,4 @@
 int32_t crServerSetOffscreenRenderingMode(GLuint value);
 void crServerRedirMuralFBO(CRMuralInfo *mural, GLuint redir);
-void crServerEnableDisplayMuralFBO(CRMuralInfo *mural, GLboolean fEnable);
 void crServerDeleteMuralFBO(CRMuralInfo *mural);
 void crServerPresentFBO(CRMuralInfo *mural);
@@ -195,4 +195,183 @@
 void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo );
 
+PCR_BLITTER crServerVBoxBlitterGet();
+
+DECLINLINE(void) crServerVBoxBlitterWinInit(CR_BLITTER_WINDOW *win, CRMuralInfo *mural)
+{
+    win->Base.id = mural->spuWindow;
+    win->Base.visualBits = mural->CreateInfo.visualBits;
+    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.visualBits;
+}
+
+/* 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_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;
+    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) cr_serverCtxSwitchPrepare(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 = pCurrentMural->aidFBOs[pCurrentMural->iCurDrawBuffer];
+        idReadFBO = pCurrentMural->aidFBOs[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) cr_serverCtxSwitchPostprocess(CR_SERVER_CTX_SWITCH *pData)
+{
+    crStateSwitchPostprocess(pData->pOldCtx, pData->pNewCtx, pData->idDrawFBO, pData->idReadFBO);
+}
 RT_C_DECLS_END
 
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c	(revision 45348)
@@ -54,4 +54,5 @@
     cr_server.fPresentMode = CR_SERVER_REDIR_F_NONE;
     cr_server.fPresentModeDefault = cr_server.fPresentMode;
+    cr_server.fVramPresentModeDefault = CR_SERVER_REDIR_F_FBO_RAM;
     cr_server.bUsePBOForReadback = GL_FALSE;
     cr_server.bUseOutputRedirect = GL_FALSE;
@@ -234,4 +235,5 @@
 #endif
     cr_server.fPresentModeDefault = cr_server.fPresentMode;
+    cr_server.fVramPresentModeDefault = CR_SERVER_REDIR_F_FBO_RAM/* | CR_SERVER_REDIR_F_FBO_RPW*/;
 
     /* Need to do this as early as possible */
@@ -377,4 +379,5 @@
 #endif
     cr_server.fPresentModeDefault = cr_server.fPresentMode;
+    cr_server.fVramPresentModeDefault = CR_SERVER_REDIR_F_FBO_RAM/* | CR_SERVER_REDIR_F_FBO_RPW*/;
 
     cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]);
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c	(revision 45348)
@@ -206,4 +206,9 @@
     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 */
@@ -361,4 +366,6 @@
     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)
@@ -460,4 +467,6 @@
     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");
@@ -2543,5 +2552,5 @@
 {
     return crServerSetOffscreenRenderingMode(value ?
-            cr_server.fPresentModeDefault | CR_SERVER_REDIR_F_FBO_RAM_VRDP
+            cr_server.fPresentModeDefault | CR_SERVER_REDIR_F_FBO_RAM_VRDP | cr_server.fVramPresentModeDefault
             : cr_server.fPresentModeDefault);
 }
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c	(revision 45348)
@@ -743,12 +743,4 @@
 }
 
-void crServerVBoxBlitterWinInit(CRMuralInfo *mural, CR_BLITTER_WINDOW *win)
-{
-    win->Base.id = mural->spuWindow;
-    win->Base.visualBits = mural->CreateInfo.visualBits;
-    win->width = mural->width;
-    win->height = mural->height;
-}
-
 int crServerVBoxBlitterBlitCurrentCtx(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
@@ -812,12 +804,9 @@
     }
 
-    crServerVBoxBlitterWinInit(mural, &BltInfo);
+    crServerVBoxBlitterWinInit(&BltInfo, mural);
+
+    crServerVBoxBlitterCtxInit(&Ctx, cr_server.curClient->currentCtxInfo);
 
     CrBltMuralSetCurrent(pBlitter, &BltInfo);
-
-    Ctx.Base.id = cr_server.curClient->currentCtxInfo->SpuContext;
-    if (Ctx.Base.id < 0)
-        Ctx.Base.id = cr_server.MainContextInfo.SpuContext;
-    Ctx.Base.visualBits = cr_server.curClient->currentCtxInfo->CreateInfo.visualBits;
 
     idDrawFBO = mural->aidFBOs[mural->iCurDrawBuffer];
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c	(revision 45347)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c	(revision 45348)
@@ -204,5 +204,5 @@
         fPresentMode &= ~CR_SERVER_REDIR_F_DISPLAY;
     else if (overlappingScreenCount > 1)
-        fPresentMode = (fPresentMode | CR_SERVER_REDIR_F_FBO_RAM_VMFB) & ~CR_SERVER_REDIR_F_DISPLAY;
+        fPresentMode = (fPresentMode | CR_SERVER_REDIR_F_FBO_RAM_VMFB | cr_server.fVramPresentModeDefault) & ~CR_SERVER_REDIR_F_DISPLAY;
 
     fPresentMode = crServerRedirModeAdjust(fPresentMode);
@@ -261,10 +261,86 @@
 }
 
+static void crServerPresentMuralVRAM(CRMuralInfo *mural, char *pixels);
+
+#define CR_SERVER_MURAL_FROM_RPW_ENTRY(_pEntry) ((CRMuralInfo*)(((uint8_t*)(_pEntry)) - RT_OFFSETOF(CRMuralInfo, RpwEntry)))
+
+static DECLCALLBACK(void) crServerMuralRpwDataCB(const struct CR_SERVER_RPW_ENTRY* pEntry, void *pvEntryTexData)
+{
+    CRMuralInfo *pMural = CR_SERVER_MURAL_FROM_RPW_ENTRY(pEntry);
+
+    Assert(&pMural->RpwEntry == pEntry);
+
+    crServerPresentMuralVRAM(pMural, pvEntryTexData);
+}
+
 static void crServerCreateMuralFBO(CRMuralInfo *mural);
 
-void crServerEnableDisplayMuralFBO(CRMuralInfo *mural, GLboolean fEnable)
+static bool crServerEnableMuralRpw(CRMuralInfo *mural, GLboolean fEnable)
 {
     if (!mural->CreateInfo.externalID)
-        return;
+    {
+        crWarning("trying to change Rpw setting for internal mural %d", mural->spuWindow);
+        return !fEnable;
+    }
+
+    if (fEnable)
+    {
+        if (!(mural->fPresentMode & CR_SERVER_REDIR_F_FBO_RPW))
+        {
+            int rc;
+            if (!crServerRpwIsInitialized(&cr_server.RpwWorker))
+            {
+                rc = crServerRpwInit(&cr_server.RpwWorker);
+                if (!RT_SUCCESS(rc))
+                {
+                    crWarning("crServerRpwInit failed rc %d", rc);
+                    return false;
+                }
+            }
+
+            CRASSERT(!mural->RpwEntry.Size.cx);
+            CRASSERT(!mural->RpwEntry.Size.cy);
+
+            if (!crServerRpwEntryIsInitialized(&mural->RpwEntry))
+            {
+                rc = crServerRpwEntryInit(&cr_server.RpwWorker, &mural->RpwEntry, mural->width, mural->height, crServerMuralRpwDataCB);
+                if (!RT_SUCCESS(rc))
+                {
+                    crWarning("crServerRpwEntryInit failed rc %d", rc);
+                    return false;
+                }
+            }
+            else
+            {
+                rc = crServerRpwEntryResize(&cr_server.RpwWorker, &mural->RpwEntry, mural->width, mural->height);
+                if (!RT_SUCCESS(rc))
+                {
+                    crWarning("crServerRpwEntryResize failed rc %d", rc);
+                    return false;
+                }
+            }
+
+            mural->fPresentMode |= CR_SERVER_REDIR_F_FBO_RPW;
+        }
+    }
+    else
+    {
+        if ((mural->fPresentMode & CR_SERVER_REDIR_F_FBO_RPW))
+        {
+//            crServerRpwEntryCleanup(&cr_server.RpwWorker, &mural->RpwEntry);
+            mural->fPresentMode &= ~CR_SERVER_REDIR_F_FBO_RPW;
+        }
+    }
+
+    return true;
+}
+
+static void crServerEnableDisplayMuralFBO(CRMuralInfo *mural, GLboolean fEnable)
+{
+    if (!mural->CreateInfo.externalID)
+    {
+        crWarning("trying to change display setting for internal mural %d", mural->spuWindow);
+        return;
+    }
 
     if (fEnable)
@@ -290,4 +366,10 @@
 void crServerRedirMuralFBO(CRMuralInfo *mural, GLuint redir)
 {
+    if (!mural->CreateInfo.externalID)
+    {
+        crWarning("trying to change redir setting for internal mural %d", mural->spuWindow);
+        return;
+    }
+
     if (mural->fPresentMode == redir)
     {
@@ -344,4 +426,6 @@
         }
     }
+
+    crServerEnableMuralRpw(mural, !!(redir & CR_SERVER_REDIR_F_FBO_RPW));
 
     crServerEnableDisplayMuralFBO(mural, !!(redir & CR_SERVER_REDIR_F_DISPLAY));
@@ -383,4 +467,10 @@
     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)
     {
@@ -391,8 +481,4 @@
         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);
@@ -463,4 +549,13 @@
     }
 
+    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)]);
 
@@ -502,4 +597,7 @@
 
     mural->cBuffers = 0;
+
+    if (crServerRpwEntryIsInitialized(&mural->RpwEntry))
+        crServerRpwEntryCleanup(&cr_server.RpwWorker, &mural->RpwEntry);
 }
 
@@ -560,5 +658,5 @@
     CRContextInfo *curCtxInfo = cr_server.currentCtxInfo;
     GLuint idDrawFBO, idReadFBO;
-    CRContext *curCtx = curCtxInfo->pContext;
+    CRContext *curCtx = curCtxInfo ? curCtxInfo->pContext : NULL;
 
     CRASSERT(curCtx == crStateGetCurrent());
@@ -655,53 +753,9 @@
 }
 
-void crServerPresentFBO(CRMuralInfo *mural)
-{
-    char *pixels=NULL, *tmppixels;
+static void crServerPresentMuralVRAM(CRMuralInfo *mural, char *pixels)
+{
+    char *tmppixels;
+    CRrecti rect, rectwr, sectr;
     int i, j;
-    CRrecti rect, rectwr, sectr;
-    GLuint idPBO;
-    CRContext *ctx = crStateGetCurrent();
-    VBOXVR_TEXTURE Tex;
-
-    CRASSERT(mural->fPresentMode & CR_SERVER_REDIR_F_FBO);
-    CRASSERT(cr_server.pfnPresentFBO || (mural->fPresentMode & CR_SERVER_REDIR_F_DISPLAY));
-
-    if (!crServerVBoxCompositionPresentNeeded(mural))
-        return;
-
-    if (mural->fPresentMode & CR_SERVER_REDIR_F_DISPLAY)
-    {
-        crServerVBoxCompositionPresentPerform(mural);
-    }
-
-    mural->fDataPresented = GL_TRUE;
-
-    if (!(mural->fPresentMode & CR_SERVER_REDIR_FGROUP_REQUIRE_FBO_RAM))
-        return;
-
-    Tex.width = mural->width;
-    Tex.height = mural->height;
-    Tex.target = GL_TEXTURE_2D;
-    Tex.hwid = mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)];
-    CRASSERT(Tex.hwid);
-
-    if (cr_server.bUsePBOForReadback && !mural->idPBO)
-    {
-        crWarning("Mural doesn't have PBO even though bUsePBOForReadback is set!");
-    }
-
-    idPBO = cr_server.bUsePBOForReadback ? mural->idPBO : 0;
-    if (idPBO)
-    {
-        CRASSERT(mural->fboWidth == mural->width);
-        CRASSERT(mural->fboHeight == mural->height);
-    }
-
-    pixels = CrHlpGetTexImage(ctx, &Tex, idPBO, GL_BGRA);
-    if (!pixels)
-    {
-        crWarning("CrHlpGetTexImage failed in crServerPresentFBO");
-        return;
-    }
 
     if (mural->fPresentMode & CR_SERVER_REDIR_F_FBO_RAM_VMFB)
@@ -766,4 +820,124 @@
                                            4 * mural->fboWidth * mural->fboHeight);
     }
+}
+
+void crServerPresentFBO(CRMuralInfo *mural)
+{
+    char *pixels=NULL;
+    GLuint idPBO;
+    CRContext *ctx = crStateGetCurrent();
+    VBOXVR_TEXTURE Tex;
+
+    CRASSERT(mural->fPresentMode & CR_SERVER_REDIR_F_FBO);
+    CRASSERT(cr_server.pfnPresentFBO || (mural->fPresentMode & CR_SERVER_REDIR_F_DISPLAY));
+
+    if (!crServerVBoxCompositionPresentNeeded(mural))
+        return;
+
+    if (mural->fPresentMode & CR_SERVER_REDIR_F_DISPLAY)
+    {
+        crServerVBoxCompositionPresentPerform(mural);
+    }
+
+    mural->fDataPresented = GL_TRUE;
+
+    if (!(mural->fPresentMode & CR_SERVER_REDIR_FGROUP_REQUIRE_FBO_RAM))
+        return;
+
+    Tex.width = mural->width;
+    Tex.height = mural->height;
+    Tex.target = GL_TEXTURE_2D;
+    Tex.hwid = mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)];
+    CRASSERT(Tex.hwid);
+
+    if (mural->fPresentMode & CR_SERVER_REDIR_F_FBO_RPW)
+    {
+        /* 1. blit to RPW entry draw texture */
+        CRMuralInfo *pCurrentMural = cr_server.currentMural;
+        CRContextInfo *pCurCtxInfo = cr_server.currentCtxInfo;
+        PCR_BLITTER pBlitter = crServerVBoxBlitterGet();
+        CRMuralInfo *pBlitterMural;
+        CR_SERVER_CTX_SWITCH CtxSwitch;
+        RTRECT Rect;
+        VBOXVR_TEXTURE DstTex;
+        CR_BLITTER_WINDOW BlitterBltInfo, CurrentBltInfo;
+        CR_BLITTER_CONTEXT CtxBltInfo;
+        int rc;
+
+        Rect.xLeft = 0;
+        Rect.yTop = 0;
+        Rect.xRight = Tex.width;
+        Rect.yBottom = Tex.height;
+
+        if (pCurrentMural && pCurrentMural->CreateInfo.visualBits == CrBltGetVisBits(pBlitter))
+        {
+            pBlitterMural = pCurrentMural;
+        }
+        else
+        {
+            pBlitterMural = crServerGetDummyMural(pCurrentMural->CreateInfo.visualBits);
+            if (!pBlitterMural)
+            {
+                crWarning("crServerGetDummyMural failed for blitter mural");
+                return;
+            }
+        }
+
+        crServerRpwEntryDrawSettingsToTex(&mural->RpwEntry, &DstTex);
+
+        cr_serverCtxSwitchPrepare(&CtxSwitch, NULL);
+
+        crServerVBoxBlitterWinInit(&CurrentBltInfo, pCurrentMural);
+        crServerVBoxBlitterWinInit(&BlitterBltInfo, pBlitterMural);
+        crServerVBoxBlitterCtxInit(&CtxBltInfo, pCurCtxInfo);
+
+        CrBltMuralSetCurrent(pBlitter, &BlitterBltInfo);
+
+        rc =  CrBltEnter(pBlitter, &CtxBltInfo, &CurrentBltInfo);
+        if (RT_SUCCESS(rc))
+        {
+            CrBltBlitTexTex(pBlitter, &Tex, &Rect, &DstTex, &Rect, 1, 0);
+            CrBltLeave(pBlitter);
+        }
+        else
+        {
+            crWarning("CrBltEnter failed rc %d", rc);
+        }
+
+        cr_serverCtxSwitchPostprocess(&CtxSwitch);
+
+        if (RT_SUCCESS(rc))
+        {
+            /* 2. submit RPW entry */
+            rc =  crServerRpwEntrySubmit(&cr_server.RpwWorker, &mural->RpwEntry);
+            if (!RT_SUCCESS(rc))
+            {
+                crWarning("crServerRpwEntrySubmit failed rc %d", rc);
+            }
+        }
+
+        return;
+    }
+
+    if (cr_server.bUsePBOForReadback && !mural->idPBO)
+    {
+        crWarning("Mural doesn't have PBO even though bUsePBOForReadback is set!");
+    }
+
+    idPBO = cr_server.bUsePBOForReadback ? mural->idPBO : 0;
+    if (idPBO)
+    {
+        CRASSERT(mural->fboWidth == mural->width);
+        CRASSERT(mural->fboHeight == mural->height);
+    }
+
+    pixels = CrHlpGetTexImage(ctx, &Tex, idPBO, GL_BGRA);
+    if (!pixels)
+    {
+        crWarning("CrHlpGetTexImage failed in crServerPresentFBO");
+        return;
+    }
+
+    crServerPresentMuralVRAM(mural, pixels);
 
     CrHlpFreeTexImage(ctx, idPBO, pixels);
Index: /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp	(revision 45348)
+++ /trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp	(revision 45348)
@@ -0,0 +1,733 @@
+/* $Id$ */
+
+/** @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.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;
+
+    CRASSERT(pDummyMural);
+
+    int rc = RTSemEventSignal(pWorker->hSubmitEvent);
+    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;
+        }
+
+        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 (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;
+            }
+        }
+    }
+
+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.visualBits);
+                CRASSERT(cr_server.MainContextInfo.SpuContext);
+
+                pWorker->ctxId = cr_server.head_spu->dispatch_table.CreateContext("", cr_server.MainContextInfo.CreateInfo.visualBits, cr_server.MainContextInfo.SpuContext);
+                if (pWorker->ctxId)
+                {
+                    CRMuralInfo *pDummyMural;
+                    pWorker->ctxVisBits = cr_server.MainContextInfo.CreateInfo.visualBits;
+                    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;
+
+                        cr_serverCtxSwitchPrepare(&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);
+
+                        cr_serverCtxSwitchPostprocess(&CtxSwitch);
+
+                        rc = RTThreadCreate(&pWorker->hThread, crServerRpwWorkerThread, pWorker, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CrServerDw");
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = RTSemEventWait(pWorker->hSubmitEvent, 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.visualBits);
+        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.visualBits);
+        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;
+}
