VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.c@ 41404

Last change on this file since 41404 was 41404, checked in by vboxsync, 13 years ago

crOpenGL: VM window scroll handling

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.3 KB
Line 
1/* $Id: server_muralfbo.c 41404 2012-05-22 16:41:38Z vboxsync $ */
2
3/** @file
4 * VBox crOpenGL: Window to FBO redirect support.
5 */
6
7/*
8 * Copyright (C) 2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "server.h"
20#include "cr_string.h"
21#include "cr_mem.h"
22#include "render/renderspu.h"
23
24static int crServerGetPointScreen(GLint x, GLint y)
25{
26 int i;
27
28 for (i=0; i<cr_server.screenCount; ++i)
29 {
30 if ((x>=cr_server.screen[i].x && x<cr_server.screen[i].x+(int)cr_server.screen[i].w)
31 && (y>=cr_server.screen[i].y && y<cr_server.screen[i].y+(int)cr_server.screen[i].h))
32 {
33 return i;
34 }
35 }
36
37 return -1;
38}
39
40static GLboolean crServerMuralCoverScreen(CRMuralInfo *mural, int sId)
41{
42 return mural->gX < cr_server.screen[sId].x
43 && mural->gX+(int)mural->width > cr_server.screen[sId].x+(int)cr_server.screen[sId].w
44 && mural->gY < cr_server.screen[sId].y
45 && mural->gY+(int)mural->height > cr_server.screen[sId].y+(int)cr_server.screen[sId].h;
46}
47
48/* Called when a new CRMuralInfo is created
49 * or when OutputRedirect status is changed.
50 */
51void crServerSetupOutputRedirect(CRMuralInfo *mural)
52{
53 /* Unset the previous redirect. */
54 if (mural->pvOutputRedirectInstance)
55 {
56 cr_server.outputRedirect.CROREnd(mural->pvOutputRedirectInstance);
57 mural->pvOutputRedirectInstance = NULL;
58 }
59
60 /* Setup a new redirect. */
61 if (cr_server.bUseOutputRedirect)
62 {
63 /* Query supported formats. */
64 uint32_t cbFormats = 4096;
65 char *pachFormats = (char *)crAlloc(cbFormats);
66
67 if (pachFormats)
68 {
69 int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext,
70 0 /* H3DOR_PROP_FORMATS */, // @todo from a header
71 pachFormats, cbFormats, &cbFormats);
72 if (RT_SUCCESS(rc))
73 {
74 if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN"))
75 {
76 cr_server.outputRedirect.CRORBegin(cr_server.outputRedirect.pvContext,
77 &mural->pvOutputRedirectInstance,
78 "H3DOR_FMT_RGBA_TOPDOWN"); // @todo from a header
79 }
80 }
81
82 crFree(pachFormats);
83 }
84
85 /* If this is not NULL then there was a supported format. */
86 if (mural->pvOutputRedirectInstance)
87 {
88 cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance,
89 mural->hX, mural->hY,
90 mural->width, mural->height);
91 // @todo the code assumes that RTRECT == four of GLInts
92 cr_server.outputRedirect.CRORVisibleRegion(mural->pvOutputRedirectInstance,
93 mural->cVisibleRects, (RTRECT *)mural->pVisibleRects);
94 }
95 }
96}
97
98void crServerCheckMuralGeometry(CRMuralInfo *mural)
99{
100 int tlS, brS, trS, blS;
101 int overlappingScreenCount, primaryS, i;
102
103 if (cr_server.screenCount<2 && !cr_server.bForceOffscreenRendering)
104 {
105 CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId];
106 CRASSERT(cr_server.screenCount>0);
107
108 mural->hX = mural->gX-cr_server.screen[0].x;
109 mural->hY = mural->gY-cr_server.screen[0].y;
110
111 cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y);
112
113 return;
114 }
115
116 tlS = crServerGetPointScreen(mural->gX, mural->gY);
117 brS = crServerGetPointScreen(mural->gX+mural->width-1, mural->gY+mural->height-1);
118
119 if (tlS==brS && tlS>=0)
120 {
121 overlappingScreenCount = 1;
122 primaryS = tlS;
123 }
124 else
125 {
126 trS = crServerGetPointScreen(mural->gX+mural->width-1, mural->gY);
127 blS = crServerGetPointScreen(mural->gX, mural->gY+mural->height-1);
128
129 primaryS = -1; overlappingScreenCount = 0;
130 for (i=0; i<cr_server.screenCount; ++i)
131 {
132 if ((i==tlS) || (i==brS) || (i==trS) || (i==blS)
133 || crServerMuralCoverScreen(mural, i))
134 {
135 overlappingScreenCount++;
136 primaryS = primaryS<0 ? i:primaryS;
137 }
138 }
139
140 if (!overlappingScreenCount)
141 {
142 primaryS = 0;
143 }
144 }
145
146 if (primaryS!=mural->screenId)
147 {
148 mural->screenId = primaryS;
149
150 renderspuSetWindowId(cr_server.screen[primaryS].winID);
151 renderspuReparentWindow(mural->spuWindow);
152 renderspuSetWindowId(cr_server.screen[0].winID);
153 }
154
155 mural->hX = mural->gX-cr_server.screen[primaryS].x;
156 mural->hY = mural->gY-cr_server.screen[primaryS].y;
157
158 if (overlappingScreenCount<2 && !cr_server.bForceOffscreenRendering)
159 {
160 CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId];
161
162 if (mural->bUseFBO)
163 {
164 crServerRedirMuralFBO(mural, GL_FALSE);
165 crServerDeleteMuralFBO(mural);
166 }
167
168 cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y);
169 }
170 else
171 {
172 if (mural->spuWindow)
173 {
174 if (!mural->bUseFBO)
175 {
176 crServerRedirMuralFBO(mural, GL_TRUE);
177 }
178 else
179 {
180 if (mural->width!=mural->fboWidth
181 || mural->height!=mural->height)
182 {
183 crServerRedirMuralFBO(mural, GL_FALSE);
184 crServerDeleteMuralFBO(mural);
185 crServerRedirMuralFBO(mural, GL_TRUE);
186 }
187 }
188 }
189#ifdef DEBUG_misha
190 else
191 {
192 Assert(!mural->bUseFBO);
193 }
194#endif
195
196 if (!mural->bUseFBO)
197 {
198 CRScreenViewportInfo *pVieport = &cr_server.screenVieport[mural->screenId];
199
200 cr_server.head_spu->dispatch_table.WindowPosition(mural->spuWindow, mural->hX - pVieport->x, mural->hY - pVieport->y);
201 }
202 }
203
204 if (mural->pvOutputRedirectInstance)
205 {
206 cr_server.outputRedirect.CRORGeometry(mural->pvOutputRedirectInstance,
207 mural->hX, mural->hY,
208 mural->width, mural->height);
209 }
210}
211
212GLboolean crServerSupportRedirMuralFBO(void)
213{
214 static GLboolean fInited = GL_FALSE;
215 static GLboolean fSupported = GL_FALSE;
216 if (!fInited)
217 {
218 const GLubyte* pExt = cr_server.head_spu->dispatch_table.GetString(GL_REAL_EXTENSIONS);
219
220 fSupported = ( NULL!=crStrstr((const char*)pExt, "GL_ARB_framebuffer_object")
221 || NULL!=crStrstr((const char*)pExt, "GL_EXT_framebuffer_object"))
222 && NULL!=crStrstr((const char*)pExt, "GL_ARB_texture_non_power_of_two");
223 fInited = GL_TRUE;
224 }
225 return fSupported;
226}
227
228void crServerRedirMuralFBO(CRMuralInfo *mural, GLboolean redir)
229{
230 if (redir)
231 {
232 if (!crServerSupportRedirMuralFBO())
233 {
234 crWarning("FBO not supported, can't redirect window output");
235 return;
236 }
237
238 cr_server.head_spu->dispatch_table.WindowShow(mural->spuWindow, GL_FALSE);
239
240 if (mural->idFBO==0)
241 {
242 crServerCreateMuralFBO(mural);
243 }
244
245 if (!crStateGetCurrent()->framebufferobject.drawFB)
246 {
247 cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, mural->idFBO);
248 }
249 if (!crStateGetCurrent()->framebufferobject.readFB)
250 {
251 cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, mural->idFBO);
252 }
253
254 crStateGetCurrent()->buffer.width = 0;
255 crStateGetCurrent()->buffer.height = 0;
256 }
257 else
258 {
259 cr_server.head_spu->dispatch_table.WindowShow(mural->spuWindow, mural->bVisible);
260
261 if (mural->bUseFBO && crServerSupportRedirMuralFBO())
262 {
263 if (!crStateGetCurrent()->framebufferobject.drawFB)
264 {
265 cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
266 }
267 if (!crStateGetCurrent()->framebufferobject.readFB)
268 {
269 cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0);
270 }
271 }
272
273 crStateGetCurrent()->buffer.width = mural->width;
274 crStateGetCurrent()->buffer.height = mural->height;
275 }
276
277 mural->bUseFBO = redir;
278}
279
280void crServerCreateMuralFBO(CRMuralInfo *mural)
281{
282 CRContext *ctx = crStateGetCurrent();
283 GLuint uid;
284 GLenum status;
285 SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table;
286
287 CRASSERT(mural->idFBO==0);
288
289 /*Color texture*/
290 gl->GenTextures(1, &mural->idColorTex);
291 gl->BindTexture(GL_TEXTURE_2D, mural->idColorTex);
292 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
293 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
294 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
295 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
296 if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
297 {
298 gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
299 }
300 gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mural->width, mural->height,
301 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
302
303 /*Depth&Stencil*/
304 gl->GenRenderbuffersEXT(1, &mural->idDepthStencilRB);
305 gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
306 gl->RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
307 mural->width, mural->height);
308
309 /*FBO*/
310 gl->GenFramebuffersEXT(1, &mural->idFBO);
311 gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mural->idFBO);
312
313 gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
314 GL_TEXTURE_2D, mural->idColorTex, 0);
315 gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
316 GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
317 gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
318 GL_RENDERBUFFER_EXT, mural->idDepthStencilRB);
319
320 status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
321 if (status!=GL_FRAMEBUFFER_COMPLETE_EXT)
322 {
323 crWarning("FBO status(0x%x) isn't complete", status);
324 }
325
326 mural->fboWidth = mural->width;
327 mural->fboHeight = mural->height;
328
329 /*PBO*/
330 if (cr_server.bUsePBOForReadback)
331 {
332 gl->GenBuffersARB(1, &mural->idPBO);
333 gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, mural->idPBO);
334 gl->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, mural->width*mural->height*4, 0, GL_STREAM_READ_ARB);
335 gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
336
337 if (!mural->idPBO)
338 {
339 crWarning("PBO create failed");
340 }
341 }
342
343 /*Restore gl state*/
344 uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid;
345 gl->BindTexture(GL_TEXTURE_2D, uid);
346
347 uid = ctx->framebufferobject.renderbuffer ? ctx->framebufferobject.renderbuffer->hwid:0;
348 gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, uid);
349
350 uid = ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0;
351 gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, uid);
352
353 uid = ctx->framebufferobject.readFB ? ctx->framebufferobject.readFB->hwid:0;
354 gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER, uid);
355
356 if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB))
357 {
358 gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid);
359 }
360}
361
362void crServerDeleteMuralFBO(CRMuralInfo *mural)
363{
364 CRASSERT(!mural->bUseFBO);
365
366 if (mural->idFBO!=0)
367 {
368 cr_server.head_spu->dispatch_table.DeleteTextures(1, &mural->idColorTex);
369 cr_server.head_spu->dispatch_table.DeleteRenderbuffersEXT(1, &mural->idDepthStencilRB);
370 cr_server.head_spu->dispatch_table.DeleteFramebuffersEXT(1, &mural->idFBO);
371
372 mural->idFBO = 0;
373 mural->idColorTex = 0;
374 mural->idDepthStencilRB = 0;
375 }
376
377 if (mural->idPBO!=0)
378 {
379 CRASSERT(cr_server.bUsePBOForReadback);
380 cr_server.head_spu->dispatch_table.DeleteBuffersARB(1, &mural->idPBO);
381 mural->idPBO = 0;
382 }
383}
384
385#define MIN(a, b) ((a) < (b) ? (a) : (b))
386#define MAX(a, b) ((a) > (b) ? (a) : (b))
387
388static GLboolean crServerIntersectRect(CRrecti *a, CRrecti *b, CRrecti *rect)
389{
390 CRASSERT(a && b && rect);
391
392 rect->x1 = MAX(a->x1, b->x1);
393 rect->x2 = MIN(a->x2, b->x2);
394 rect->y1 = MAX(a->y1, b->y1);
395 rect->y2 = MIN(a->y2, b->y2);
396
397 return (rect->x2>rect->x1) && (rect->y2>rect->y1);
398}
399
400static GLboolean crServerIntersectScreen(CRMuralInfo *mural, int sId, CRrecti *rect)
401{
402 rect->x1 = MAX(mural->gX, cr_server.screen[sId].x);
403 rect->x2 = MIN(mural->gX+(int)mural->fboWidth, cr_server.screen[sId].x+(int)cr_server.screen[sId].w);
404 rect->y1 = MAX(mural->gY, cr_server.screen[sId].y);
405 rect->y2 = MIN(mural->gY+(int)mural->fboHeight, cr_server.screen[sId].y+(int)cr_server.screen[sId].h);
406
407 return (rect->x2>rect->x1) && (rect->y2>rect->y1);
408}
409
410static void crServerCopySubImage(char *pDst, char* pSrc, CRrecti *pRect, int srcWidth, int srcHeight)
411{
412 int i;
413 int dstrowsize = 4*(pRect->x2-pRect->x1);
414 int srcrowsize = 4*srcWidth;
415 int height = pRect->y2-pRect->y1;
416
417 pSrc += 4*pRect->x1 + srcrowsize*(srcHeight-1-pRect->y1);
418
419 for (i=0; i<height; ++i)
420 {
421 crMemcpy(pDst, pSrc, dstrowsize);
422
423 pSrc -= srcrowsize;
424 pDst += dstrowsize;
425 }
426}
427
428static void crServerTransformRect(CRrecti *pDst, CRrecti *pSrc, int dx, int dy)
429{
430 pDst->x1 = pSrc->x1+dx;
431 pDst->x2 = pSrc->x2+dx;
432 pDst->y1 = pSrc->y1+dy;
433 pDst->y2 = pSrc->y2+dy;
434}
435
436void crServerPresentFBO(CRMuralInfo *mural)
437{
438 char *pixels=NULL, *tmppixels;
439 GLuint uid;
440 int i, j;
441 CRrecti rect, rectwr, sectr;
442 GLboolean bUsePBO;
443 CRContext *ctx = crStateGetCurrent();
444
445 CRASSERT(cr_server.pfnPresentFBO);
446
447 if (!mural->bVisible)
448 {
449 return;
450 }
451
452 if (!mural->width || !mural->height)
453 {
454 return;
455 }
456
457 if (cr_server.bUsePBOForReadback && !mural->idPBO)
458 {
459 crWarning("Mural doesn't have PBO even though bUsePBOForReadback is set!");
460 }
461
462 bUsePBO = cr_server.bUsePBOForReadback && mural->idPBO;
463
464 cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, mural->idColorTex);
465
466 if (bUsePBO)
467 {
468 CRASSERT(mural->idPBO);
469 cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, mural->idPBO);
470 }
471 else
472 {
473 if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
474 {
475 cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
476 }
477
478 pixels = crAlloc(4*mural->fboWidth*mural->fboHeight);
479 if (!pixels)
480 {
481 crWarning("Out of memory in crServerPresentFBO");
482 return;
483 }
484 }
485
486 /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/
487 cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
488
489 /*restore gl state*/
490 uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid;
491 cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, uid);
492
493 if (bUsePBO)
494 {
495 pixels = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
496 if (!pixels)
497 {
498 crWarning("Failed to MapBuffer in crServerPresentFBO");
499 return;
500 }
501 }
502
503 for (i=0; i<cr_server.screenCount; ++i)
504 {
505 if (crServerIntersectScreen(mural, i, &rect))
506 {
507 /* rect in window relative coords */
508 crServerTransformRect(&rectwr, &rect, -mural->gX, -mural->gY);
509
510 if (!mural->pVisibleRects)
511 {
512 /*we don't get any rects info for guest compiz windows, so we treat windows as visible unless explicitly received 0 visible rects*/
513 if (!mural->bReceivedRects)
514 {
515 tmppixels = crAlloc(4*(rect.x2-rect.x1)*(rect.y2-rect.y1));
516 if (!tmppixels)
517 {
518 crWarning("Out of memory in crServerPresentFBO");
519 crFree(pixels);
520 return;
521 }
522
523 crServerCopySubImage(tmppixels, pixels, &rectwr, mural->fboWidth, mural->fboHeight);
524 /*Note: pfnPresentFBO would free tmppixels*/
525 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);
526 }
527 }
528 else
529 {
530 for (j=0; j<mural->cVisibleRects; ++j)
531 {
532 if (crServerIntersectRect(&rectwr, (CRrecti*) &mural->pVisibleRects[4*j], &sectr))
533 {
534 tmppixels = crAlloc(4*(sectr.x2-sectr.x1)*(sectr.y2-sectr.y1));
535 if (!tmppixels)
536 {
537 crWarning("Out of memory in crServerPresentFBO");
538 crFree(pixels);
539 return;
540 }
541
542 crServerCopySubImage(tmppixels, pixels, &sectr, mural->fboWidth, mural->fboHeight);
543 /*Note: pfnPresentFBO would free tmppixels*/
544 cr_server.pfnPresentFBO(tmppixels, i,
545 sectr.x1+mural->gX-cr_server.screen[i].x,
546 sectr.y1+mural->gY-cr_server.screen[i].y,
547 sectr.x2-sectr.x1, sectr.y2-sectr.y1);
548 }
549 }
550 }
551 }
552 }
553
554 if (mural->pvOutputRedirectInstance)
555 {
556 /* @todo find out why presentfbo is not called but crorframe is called. */
557 cr_server.outputRedirect.CRORFrame(mural->pvOutputRedirectInstance,
558 pixels,
559 4 * mural->fboWidth * mural->fboHeight);
560 }
561
562 if (bUsePBO)
563 {
564 cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
565 cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
566 }
567 else
568 {
569 crFree(pixels);
570 if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB))
571 {
572 cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid);
573 }
574 }
575}
576
577GLboolean crServerIsRedirectedToFBO()
578{
579 return cr_server.curClient
580 && cr_server.curClient->currentMural
581 && cr_server.curClient->currentMural->bUseFBO;
582}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette