/* $Id$ */ /** @file * Blitter API implementation */ /* * Copyright (C) 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_blitter.h" #include "cr_spu.h" #include "chromium.h" #include "cr_error.h" #include "cr_net.h" #include "cr_rand.h" #include "cr_mem.h" #include "cr_string.h" #include "cr_bmpscale.h" #include #include #include static void crMClrFillMem(uint32_t *pu32Dst, int32_t cbDstPitch, uint32_t width, uint32_t height, uint32_t u32Color) { for (uint32_t i = 0; i < height; ++i) { for (uint32_t j = 0; j < width; ++j) { pu32Dst[j] = u32Color; } pu32Dst = (uint32_t*)(((uint8_t*)pu32Dst) + cbDstPitch); } } void CrMClrFillImgRect(CR_BLITTER_IMG *pDst, const RTRECT *pCopyRect, uint32_t u32Color) { int32_t x = pCopyRect->xLeft; int32_t y = pCopyRect->yTop; int32_t width = pCopyRect->xRight - pCopyRect->xLeft; int32_t height = pCopyRect->yBottom - pCopyRect->yTop; Assert(x >= 0); Assert(y >= 0); uint8_t *pu8Dst = ((uint8_t*)pDst->pvData) + pDst->pitch * y + x * 4; crMClrFillMem((uint32_t*)pu8Dst, pDst->pitch, width, height, u32Color); } void CrMClrFillImg(CR_BLITTER_IMG *pImg, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color) { RTRECT Rect; Rect.xLeft = 0; Rect.yTop = 0; Rect.xRight = pImg->width; Rect.yBottom = pImg->height; RTRECT Intersection; const RTPOINT ZeroPoint = {0, 0}; for (uint32_t i = 0; i < cRects; ++i) { const RTRECT * pRect = &pRects[i]; VBoxRectIntersected(pRect, &Rect, &Intersection); if (VBoxRectIsZero(&Intersection)) continue; CrMClrFillImgRect(pImg, &Intersection, u32Color); } } static void crMBltMem(const 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; } } void CrMBltImgRect(const CR_BLITTER_IMG *pSrc, const RTPOINT *pSrcDataPoint, bool fSrcInvert, const RTRECT *pCopyRect, 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; int32_t dstY = pCopyRect->yTop; Assert(dstX >= 0); Assert(dstY >= 0); Assert(dstX < (int32_t)pDst->width); Assert(dstY < (int32_t)pDst->height); 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; crMBltMem(pu8Src, fSrcInvert ? -((int32_t)pSrc->pitch) : (int32_t)pSrc->pitch, pu8Dst, pDst->pitch, pCopyRect->xRight - pCopyRect->xLeft, pCopyRect->yBottom - pCopyRect->yTop); } void CrMBltImg(const CR_BLITTER_IMG *pSrc, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst) { RTRECT Intersection; RTRECT RestrictSrcRect; RestrictSrcRect.xLeft = 0; RestrictSrcRect.yTop = 0; RestrictSrcRect.xRight = pSrc->width; RestrictSrcRect.yBottom = pSrc->height; RTRECT RestrictDstRect; RestrictDstRect.xLeft = 0; RestrictDstRect.yTop = 0; RestrictDstRect.xRight = pDst->width; RestrictDstRect.yBottom = pDst->height; for (uint32_t i = 0; i < cRects; ++i) { const RTRECT * pRect = &pRects[i]; VBoxRectIntersected(pRect, &RestrictDstRect, &Intersection); RTRECT TranslatedSrc; VBoxRectTranslated(&RestrictSrcRect, pPos->x, pPos->y, &TranslatedSrc); VBoxRectIntersect(&Intersection, &TranslatedSrc); if (VBoxRectIsZero(&Intersection)) continue; CrMBltImgRect(pSrc, pPos, false, &Intersection, pDst); } } void CrMBltImgRectScaled(const CR_BLITTER_IMG *pSrc, const RTPOINT *pPos, bool fSrcInvert, const RTRECT *pCopyRect, float strX, float strY, CR_BLITTER_IMG *pDst) { RTPOINT UnscaledPos; UnscaledPos.x = CR_FLOAT_RCAST(int32_t, pPos->x / strX); UnscaledPos.y = CR_FLOAT_RCAST(int32_t, pPos->y / strY); RTRECT UnscaledCopyRect; VBoxRectUnscaled(pCopyRect, strX, strY, &UnscaledCopyRect); if (VBoxRectIsZero(&UnscaledCopyRect)) { WARN(("ups")); return; } int32_t srcX = UnscaledCopyRect.xLeft - UnscaledPos.x; int32_t srcY = UnscaledCopyRect.yTop - UnscaledPos.y; if (srcX < 0) { WARN(("ups")); srcX = 0; } if (srcY < 0) { WARN(("ups")); srcY = 0; } if (srcX >= pSrc->width) { WARN(("ups")); return; } if (srcY >= pSrc->height) { WARN(("ups")); return; } Assert(srcX >= 0); Assert(srcY >= 0); Assert(srcX < (int32_t)pSrc->width); Assert(srcY < (int32_t)pSrc->height); int32_t dstX = pCopyRect->xLeft; int32_t dstY = pCopyRect->yTop; Assert(dstX >= 0); Assert(dstY >= 0); int32_t UnscaledSrcWidth = UnscaledCopyRect.xRight - UnscaledCopyRect.xLeft; int32_t UnscaledSrcHeight = UnscaledCopyRect.yBottom - UnscaledCopyRect.yTop; if (UnscaledSrcWidth + srcX > pSrc->width) UnscaledSrcWidth = pSrc->width - srcX; if (UnscaledSrcHeight + srcY > pSrc->height) UnscaledSrcHeight = pSrc->height - srcY; 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 ? -((int32_t)pSrc->pitch) : (int32_t)pSrc->pitch, UnscaledSrcWidth, UnscaledSrcHeight); } void CrMBltImgScaled(const CR_BLITTER_IMG *pSrc, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst) { int32_t srcWidth = pSrcRectSize->cx; int32_t srcHeight = pSrcRectSize->cy; int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; float strX = ((float)dstWidth) / srcWidth; float strY = ((float)dstHeight) / srcHeight; bool fScale = (dstWidth != srcWidth || dstHeight != srcHeight); Assert(fScale); RTRECT Intersection; RTRECT ScaledRestrictSrcRect; ScaledRestrictSrcRect.xLeft = 0; ScaledRestrictSrcRect.yTop = 0; ScaledRestrictSrcRect.xRight = CR_FLOAT_RCAST(int32_t, pSrc->width * strX); ScaledRestrictSrcRect.yBottom = CR_FLOAT_RCAST(int32_t, pSrc->height * strY); RTRECT RestrictDstRect; RestrictDstRect.xLeft = 0; RestrictDstRect.yTop = 0; RestrictDstRect.xRight = pDst->width; RestrictDstRect.yBottom = pDst->height; RTPOINT Pos = {pDstRect->xLeft, pDstRect->yTop}; for (uint32_t i = 0; i < cRects; ++i) { const RTRECT * pRect = &pRects[i]; VBoxRectIntersected(pRect, &RestrictDstRect, &Intersection); RTRECT TranslatedSrc; VBoxRectTranslated(&ScaledRestrictSrcRect, Pos.x, Pos.y, &TranslatedSrc); VBoxRectIntersect(&Intersection, &TranslatedSrc); if (VBoxRectIsZero(&Intersection)) continue; CrMBltImgRectScaled(pSrc, &Pos, false, &Intersection, strX, strY, pDst); } } /* @param pCtxBase - contains the blitter context info. Its value is treated differently depending on the fCreateNewCtx value * @param fCreateNewCtx - if true - the pCtxBase must NOT be NULL. its visualBits is used as a visual bits info for the new context, * its id field is used to specified the shared context id to be used for blitter context. * The id can be null to specify no shared context is needed * if false - if pCtxBase is NOT null AND its id field is NOT null - * specified the blitter context to be used * blitter treats it as if it has default ogl state. * otherwise - * the blitter works in a "no-context" mode, i.e. a caller is responsible * to making a proper context current before calling the blitter. * Note that BltEnter/Leave MUST still be called, but the proper context * must be set before doing BltEnter, and ResoreContext info is ignored in that case. * Also note that blitter caches the current window info, and assumes the current context's values are preserved * wrt that window before the calls, so if one uses different contexts for one blitter, * the blitter current window values must be explicitly reset by doing CrBltMuralSetCurrentInfo(pBlitter, NULL) * @param fForceDrawBlt - if true - forces the blitter to always use glDrawXxx-based blits even if GL_EXT_framebuffer_blit. * This is needed because BlitFramebufferEXT is known to be often buggy, and glDrawXxx-based blits appear to be more reliable */ VBOXBLITTERDECL(int) CrBltInit(PCR_BLITTER pBlitter, const CR_BLITTER_CONTEXT *pCtxBase, bool fCreateNewCtx, bool fForceDrawBlt, const CR_GLSL_CACHE *pShaders, SPUDispatchTable *pDispatch) { if (pCtxBase && pCtxBase->Base.id < 0) { crWarning("Default share context not initialized!"); return VERR_INVALID_PARAMETER; } if (!pCtxBase && fCreateNewCtx) { crWarning("pCtxBase is zero while fCreateNewCtx is set!"); return VERR_INVALID_PARAMETER; } memset(pBlitter, 0, sizeof (*pBlitter)); pBlitter->pDispatch = pDispatch; if (pCtxBase) pBlitter->CtxInfo = *pCtxBase; pBlitter->Flags.ForceDrawBlit = fForceDrawBlt; if (fCreateNewCtx) { pBlitter->CtxInfo.Base.id = pDispatch->CreateContext("", pCtxBase->Base.visualBits, pCtxBase->Base.id); if (!pBlitter->CtxInfo.Base.id) { memset(pBlitter, 0, sizeof (*pBlitter)); crWarning("CreateContext failed!"); return VERR_GENERAL_FAILURE; } pBlitter->Flags.CtxCreated = 1; } if (pShaders) { pBlitter->pGlslCache = pShaders; pBlitter->Flags.ShadersGloal = 1; } else { CrGlslInit(&pBlitter->LocalGlslCache, pDispatch); pBlitter->pGlslCache = &pBlitter->LocalGlslCache; } return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrBltCleanup(PCR_BLITTER pBlitter) { if (CrBltIsEntered(pBlitter)) { WARN(("CrBltBlitTexTex: blitter is entered")); return VERR_INVALID_STATE; } if (pBlitter->Flags.ShadersGloal || !CrGlslNeedsCleanup(&pBlitter->LocalGlslCache)) return VINF_SUCCESS; int rc = CrBltEnter(pBlitter); if (!RT_SUCCESS(rc)) { WARN(("CrBltEnter failed, rc %d", rc)); return rc; } CrGlslCleanup(&pBlitter->LocalGlslCache); CrBltLeave(pBlitter); return VINF_SUCCESS; } void CrBltTerm(PCR_BLITTER pBlitter) { if (pBlitter->Flags.CtxCreated) pBlitter->pDispatch->DestroyContext(pBlitter->CtxInfo.Base.id); memset(pBlitter, 0, sizeof (*pBlitter)); } int CrBltMuralSetCurrentInfo(PCR_BLITTER pBlitter, const CR_BLITTER_WINDOW *pMural) { if (pMural) { if (!memcmp(&pBlitter->CurrentMural, pMural, sizeof (pBlitter->CurrentMural))) return VINF_SUCCESS; memcpy(&pBlitter->CurrentMural, pMural, sizeof (pBlitter->CurrentMural)); } else { if (CrBltIsEntered(pBlitter)) { WARN(("can not set null mural for entered bleater")); return VERR_INVALID_STATE; } if (!pBlitter->CurrentMural.Base.id) return VINF_SUCCESS; pBlitter->CurrentMural.Base.id = 0; } pBlitter->Flags.CurrentMuralChanged = 1; if (!CrBltIsEntered(pBlitter)) return VINF_SUCCESS; else if (!pBlitter->CtxInfo.Base.id) { WARN(("setting current mural for entered no-context blitter")); return VERR_INVALID_STATE; } WARN(("changing mural for entered blitter, is is somewhat expected?")); pBlitter->pDispatch->Flush(); pBlitter->pDispatch->MakeCurrent(pMural->Base.id, pBlitter->i32MakeCurrentUserData, pBlitter->CtxInfo.Base.id); return VINF_SUCCESS; } static DECLCALLBACK(int) crBltBlitTexBufImplFbo(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRect, const RTRECTSIZE *pDstSize, const RTRECT *paDstRect, uint32_t cRects, uint32_t fFlags) { GLenum filter = CRBLT_FILTER_FROM_FLAGS(fFlags); pBlitter->pDispatch->BindFramebufferEXT(GL_READ_FRAMEBUFFER, pBlitter->idFBO); pBlitter->pDispatch->FramebufferTexture2DEXT(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pSrc->target, pSrc->hwid, 0); pBlitter->pDispatch->ReadBuffer(GL_COLOR_ATTACHMENT0); for (uint32_t i = 0; i < cRects; ++i) { const RTRECT * pSrcRect = &paSrcRect[i]; const RTRECT * pDstRect = &paDstRect[i]; int32_t srcY1; int32_t srcY2; int32_t dstY1; int32_t dstY2; int32_t srcX1 = pSrcRect->xLeft; int32_t srcX2 = pSrcRect->xRight; int32_t dstX1 = pDstRect->xLeft; int32_t dstX2 = pDstRect->xRight; if (CRBLT_F_INVERT_SRC_YCOORDS & fFlags) { srcY1 = pSrc->height - pSrcRect->yTop; srcY2 = pSrc->height - pSrcRect->yBottom; } else { srcY1 = pSrcRect->yTop; srcY2 = pSrcRect->yBottom; } if (CRBLT_F_INVERT_DST_YCOORDS & fFlags) { dstY1 = pDstSize->cy - pDstRect->yTop; dstY2 = pDstSize->cy - pDstRect->yBottom; } else { dstY1 = pDstRect->yTop; dstY2 = pDstRect->yBottom; } if (srcY1 > srcY2) { if (dstY1 > dstY2) { /* use srcY1 < srcY2 && dstY1 < dstY2 whenever possible to avoid GPU driver bugs */ int32_t tmp = srcY1; srcY1 = srcY2; srcY2 = tmp; tmp = dstY1; dstY1 = dstY2; dstY2 = tmp; } } if (srcX1 > srcX2) { if (dstX1 > dstX2) { /* use srcX1 < srcX2 && dstX1 < dstX2 whenever possible to avoid GPU driver bugs */ int32_t tmp = srcX1; srcX1 = srcX2; srcX2 = tmp; tmp = dstX1; dstX1 = dstX2; dstX2 = tmp; } } pBlitter->pDispatch->BlitFramebufferEXT(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, GL_COLOR_BUFFER_BIT, filter); } return VINF_SUCCESS; } /* GL_TRIANGLE_FAN */ DECLINLINE(GLfloat*) crBltVtRectTFNormalized(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) { /* going ccw: * 1. (left;top) 4. (right;top) * | ^ * > | * 2. (left;bottom) -> 3. (right;bottom) */ /* xLeft yTop */ pBuff[0] = ((float)pRect->xLeft)/((float)normalX); pBuff[1] = ((float)(height ? height - pRect->yTop : pRect->yTop))/((float)normalY); /* xLeft yBottom */ pBuff[2] = pBuff[0]; pBuff[3] = ((float)(height ? height - pRect->yBottom : pRect->yBottom))/((float)normalY); /* xRight yBottom */ pBuff[4] = ((float)pRect->xRight)/((float)normalX); pBuff[5] = pBuff[3]; /* xRight yTop */ pBuff[6] = pBuff[4]; pBuff[7] = pBuff[1]; return &pBuff[8]; } DECLINLINE(GLfloat*) crBltVtRectsTFNormalized(const RTRECT *paRects, uint32_t cRects, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) { for (uint32_t i = 0; i < cRects; ++i) { pBuff = crBltVtRectTFNormalized(&paRects[i], normalX, normalY, pBuff, height); } return pBuff; } DECLINLINE(GLint*) crBltVtRectTF(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLint* pBuff, uint32_t height) { /* xLeft yTop */ pBuff[0] = pRect->xLeft; pBuff[1] = height ? height - pRect->yTop : pRect->yTop; /* xLeft yBottom */ pBuff[2] = pBuff[0]; pBuff[3] = height ? height - pRect->yBottom : pRect->yBottom; /* xRight yBottom */ pBuff[4] = pRect->xRight; pBuff[5] = pBuff[3]; /* xRight yTop */ pBuff[6] = pBuff[4]; pBuff[7] = pBuff[1]; return &pBuff[8]; } DECLINLINE(GLubyte*) crBltVtFillRectIndicies(GLubyte *pIndex, GLubyte *piBase) { GLubyte iBase = *piBase; /* triangle 1 */ pIndex[0] = iBase; pIndex[1] = iBase + 1; pIndex[2] = iBase + 2; /* triangle 2 */ pIndex[3] = iBase; pIndex[4] = iBase + 2; pIndex[5] = iBase + 3; *piBase = iBase + 4; return pIndex + 6; } /* Indexed GL_TRIANGLES */ DECLINLINE(GLfloat*) crBltVtRectITNormalized(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) { GLfloat* ret = crBltVtRectTFNormalized(pRect, normalX, normalY, pBuff, height); return ret; } DECLINLINE(GLint*) crBltVtRectIT(RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLint* pBuff, GLubyte **ppIndex, GLubyte *piBase, uint32_t height) { GLint* ret = crBltVtRectTF(pRect, normalX, normalY, pBuff, height); if (ppIndex) *ppIndex = crBltVtFillRectIndicies(*ppIndex, piBase); return ret; } DECLINLINE(GLuint) crBltVtGetNumVerticiesTF(GLuint cRects) { return cRects * 4; } #define crBltVtGetNumVerticiesIT crBltVtGetNumVerticiesTF DECLINLINE(GLuint) crBltVtGetNumIndiciesIT(GLuint cRects) { return 6 * cRects; } static GLfloat* crBltVtRectsITNormalized(const RTRECT *paRects, uint32_t cRects, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, GLubyte **ppIndex, GLubyte *piBase, uint32_t height) { uint32_t i; for (i = 0; i < cRects; ++i) { pBuff = crBltVtRectITNormalized(&paRects[i], normalX, normalY, pBuff, height); } if (ppIndex) { GLubyte *pIndex = (GLubyte*)pBuff; *ppIndex = pIndex; for (i = 0; i < cRects; ++i) { pIndex = crBltVtFillRectIndicies(pIndex, piBase); } pBuff = (GLfloat*)pIndex; } return pBuff; } static void* crBltBufGet(PCR_BLITTER_BUFFER pBuffer, GLuint cbBuffer) { if (pBuffer->cbBuffer < cbBuffer) { if (pBuffer->pvBuffer) { RTMemFree(pBuffer->pvBuffer); } #ifndef DEBUG_misha /* debugging: ensure we calculate proper buffer size */ cbBuffer += 16; #endif pBuffer->pvBuffer = RTMemAlloc(cbBuffer); if (pBuffer->pvBuffer) pBuffer->cbBuffer = cbBuffer; else { crWarning("failed to allocate buffer of size %d", cbBuffer); pBuffer->cbBuffer = 0; } } return pBuffer->pvBuffer; } static void crBltCheckSetupViewport(PCR_BLITTER pBlitter, const RTRECTSIZE *pDstSize, bool fFBODraw) { bool fUpdateViewport = pBlitter->Flags.CurrentMuralChanged; if (pBlitter->CurrentSetSize.cx != pDstSize->cx || pBlitter->CurrentSetSize.cy != pDstSize->cy) { pBlitter->CurrentSetSize = *pDstSize; pBlitter->pDispatch->MatrixMode(GL_PROJECTION); pBlitter->pDispatch->LoadIdentity(); pBlitter->pDispatch->Ortho(0, pDstSize->cx, 0, pDstSize->cy, -1, 1); fUpdateViewport = true; } if (fUpdateViewport) { pBlitter->pDispatch->Viewport(0, 0, pBlitter->CurrentSetSize.cx, pBlitter->CurrentSetSize.cy); pBlitter->Flags.CurrentMuralChanged = 0; } pBlitter->Flags.LastWasFBODraw = fFBODraw; } static DECLCALLBACK(int) crBltBlitTexBufImplDraw2D(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRect, const RTRECTSIZE *pDstSize, const RTRECT *paDstRect, uint32_t cRects, uint32_t fFlags) { GLuint normalX, normalY; uint32_t srcHeight = (fFlags & CRBLT_F_INVERT_SRC_YCOORDS) ? pSrc->height : 0; uint32_t dstHeight = (fFlags & CRBLT_F_INVERT_DST_YCOORDS) ? pDstSize->cy : 0; switch (pSrc->target) { case GL_TEXTURE_2D: { normalX = pSrc->width; normalY = pSrc->height; break; } case GL_TEXTURE_RECTANGLE_ARB: { normalX = 1; normalY = 1; break; } default: { crWarning("Unsupported texture target 0x%x", pSrc->target); return VERR_INVALID_PARAMETER; } } Assert(pSrc->hwid); pBlitter->pDispatch->BindTexture(pSrc->target, pSrc->hwid); if (cRects == 1) { /* just optimization to draw a single rect with GL_TRIANGLE_FAN */ GLfloat *pVerticies; GLfloat *pTexCoords; GLuint cElements = crBltVtGetNumVerticiesTF(cRects); pVerticies = (GLfloat*)crBltBufGet(&pBlitter->Verticies, cElements * 2 * 2 * sizeof (*pVerticies)); pTexCoords = crBltVtRectsTFNormalized(paDstRect, cRects, 1, 1, pVerticies, dstHeight); crBltVtRectsTFNormalized(paSrcRect, cRects, normalX, normalY, pTexCoords, srcHeight); pBlitter->pDispatch->EnableClientState(GL_VERTEX_ARRAY); pBlitter->pDispatch->VertexPointer(2, GL_FLOAT, 0, pVerticies); pBlitter->pDispatch->EnableClientState(GL_TEXTURE_COORD_ARRAY); pBlitter->pDispatch->TexCoordPointer(2, GL_FLOAT, 0, pTexCoords); pBlitter->pDispatch->Enable(pSrc->target); pBlitter->pDispatch->DrawArrays(GL_TRIANGLE_FAN, 0, cElements); pBlitter->pDispatch->Disable(pSrc->target); pBlitter->pDispatch->DisableClientState(GL_TEXTURE_COORD_ARRAY); pBlitter->pDispatch->DisableClientState(GL_VERTEX_ARRAY); } else { GLfloat *pVerticies; GLfloat *pTexCoords; GLubyte *pIndicies; GLuint cElements = crBltVtGetNumVerticiesIT(cRects); GLuint cIndicies = crBltVtGetNumIndiciesIT(cRects); GLubyte iIdxBase = 0; pVerticies = (GLfloat*)crBltBufGet(&pBlitter->Verticies, cElements * 2 * 2 * sizeof (*pVerticies) + cIndicies * sizeof (*pIndicies)); pTexCoords = crBltVtRectsITNormalized(paDstRect, cRects, 1, 1, pVerticies, &pIndicies, &iIdxBase, dstHeight); crBltVtRectsITNormalized(paSrcRect, cRects, normalX, normalY, pTexCoords, NULL, NULL, srcHeight); pBlitter->pDispatch->EnableClientState(GL_VERTEX_ARRAY); pBlitter->pDispatch->VertexPointer(2, GL_FLOAT, 0, pVerticies); pBlitter->pDispatch->EnableClientState(GL_TEXTURE_COORD_ARRAY); pBlitter->pDispatch->TexCoordPointer(2, GL_FLOAT, 0, pTexCoords); pBlitter->pDispatch->Enable(pSrc->target); pBlitter->pDispatch->DrawElements(GL_TRIANGLES, cIndicies, GL_UNSIGNED_BYTE, pIndicies); pBlitter->pDispatch->Disable(pSrc->target); pBlitter->pDispatch->DisableClientState(GL_TEXTURE_COORD_ARRAY); pBlitter->pDispatch->DisableClientState(GL_VERTEX_ARRAY); } pBlitter->pDispatch->BindTexture(pSrc->target, 0); return VINF_SUCCESS; } static int crBltInitOnMakeCurent(PCR_BLITTER pBlitter) { const char * pszExtension = (const char*)pBlitter->pDispatch->GetString(GL_EXTENSIONS); if (crStrstr(pszExtension, "GL_EXT_framebuffer_object")) { pBlitter->Flags.SupportsFBO = 1; pBlitter->pDispatch->GenFramebuffersEXT(1, &pBlitter->idFBO); Assert(pBlitter->idFBO); } else crWarning("GL_EXT_framebuffer_object not supported, blitter can only blit to window"); if (crStrstr(pszExtension, "GL_ARB_pixel_buffer_object")) pBlitter->Flags.SupportsPBO = 1; else crWarning("GL_ARB_pixel_buffer_object not supported"); /* BlitFramebuffer seems to be buggy on Intel, * try always glDrawXxx for now */ if (!pBlitter->Flags.ForceDrawBlit && crStrstr(pszExtension, "GL_EXT_framebuffer_blit")) { pBlitter->pfnBlt = crBltBlitTexBufImplFbo; } else { // crWarning("GL_EXT_framebuffer_blit not supported, will use Draw functions for blitting, which might be less efficient"); pBlitter->pfnBlt = crBltBlitTexBufImplDraw2D; } /* defaults. but just in case */ pBlitter->pDispatch->MatrixMode(GL_TEXTURE); pBlitter->pDispatch->LoadIdentity(); pBlitter->pDispatch->MatrixMode(GL_MODELVIEW); pBlitter->pDispatch->LoadIdentity(); return VINF_SUCCESS; } void CrBltLeave(PCR_BLITTER pBlitter) { if (!pBlitter->cEnters) { WARN(("blitter not entered!")); return; } if (--pBlitter->cEnters) return; if (pBlitter->Flags.SupportsFBO) { pBlitter->pDispatch->BindFramebufferEXT(GL_FRAMEBUFFER, 0); pBlitter->pDispatch->DrawBuffer(GL_BACK); pBlitter->pDispatch->ReadBuffer(GL_BACK); } pBlitter->pDispatch->Flush(); if (pBlitter->CtxInfo.Base.id) pBlitter->pDispatch->MakeCurrent(0, 0, 0); } int CrBltEnter(PCR_BLITTER pBlitter) { if (!pBlitter->CurrentMural.Base.id && pBlitter->CtxInfo.Base.id) { WARN(("current mural not initialized!")); return VERR_INVALID_STATE; } if (pBlitter->cEnters++) return VINF_SUCCESS; if (pBlitter->CurrentMural.Base.id) /* <- pBlitter->CurrentMural.Base.id can be null if the blitter is in a "no-context" mode (see comments to BltInit for detail)*/ { pBlitter->pDispatch->MakeCurrent(pBlitter->CurrentMural.Base.id, pBlitter->i32MakeCurrentUserData, pBlitter->CtxInfo.Base.id); } if (pBlitter->Flags.Initialized) return VINF_SUCCESS; int rc = crBltInitOnMakeCurent(pBlitter); if (RT_SUCCESS(rc)) { pBlitter->Flags.Initialized = 1; return VINF_SUCCESS; } WARN(("crBltInitOnMakeCurent failed, rc %d", rc)); CrBltLeave(pBlitter); return rc; } static void crBltBlitTexBuf(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRects, GLenum enmDstBuff, const RTRECTSIZE *pDstSize, const RTRECT *paDstRects, uint32_t cRects, uint32_t fFlags) { pBlitter->pDispatch->DrawBuffer(enmDstBuff); crBltCheckSetupViewport(pBlitter, pDstSize, enmDstBuff == GL_DRAW_FRAMEBUFFER); if (!(fFlags & CRBLT_F_NOALPHA)) pBlitter->pfnBlt(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); else { int rc = pBlitter->Flags.ShadersGloal ? CrGlslProgUseNoAlpha(pBlitter->pGlslCache, pSrc->target) : CrGlslProgUseGenNoAlpha(&pBlitter->LocalGlslCache, pSrc->target); if (!RT_SUCCESS(rc)) { crWarning("Failed to use no-alpha program rc %d!, falling back to default blit", rc); pBlitter->pfnBlt(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); return; } /* since we use shaders, we need to use draw commands rather than framebuffer blits. * force using draw-based blitting */ crBltBlitTexBufImplDraw2D(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); Assert(pBlitter->Flags.ShadersGloal || &pBlitter->LocalGlslCache == pBlitter->pGlslCache); CrGlslProgClear(pBlitter->pGlslCache); } } void CrBltCheckUpdateViewport(PCR_BLITTER pBlitter) { RTRECTSIZE DstSize = {pBlitter->CurrentMural.width, pBlitter->CurrentMural.height}; crBltCheckSetupViewport(pBlitter, &DstSize, false); } void CrBltBlitTexMural(PCR_BLITTER pBlitter, bool fBb, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRects, const RTRECT *paDstRects, uint32_t cRects, uint32_t fFlags) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltBlitTexMural: blitter not entered")); return; } RTRECTSIZE DstSize = {pBlitter->CurrentMural.width, pBlitter->CurrentMural.height}; pBlitter->pDispatch->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); crBltBlitTexBuf(pBlitter, pSrc, paSrcRects, fBb ? GL_BACK : GL_FRONT, &DstSize, paDstRects, cRects, fFlags); } void CrBltBlitTexTex(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *pSrcRect, const VBOXVR_TEXTURE *pDst, const RTRECT *pDstRect, uint32_t cRects, uint32_t fFlags) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltBlitTexTex: blitter not entered")); return; } RTRECTSIZE DstSize = {(uint32_t)pDst->width, (uint32_t)pDst->height}; pBlitter->pDispatch->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, pBlitter->idFBO); /* TODO: mag/min filters ? */ pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pDst->target, pDst->hwid, 0); // pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); // pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); crBltBlitTexBuf(pBlitter, pSrc, pSrcRect, GL_DRAW_FRAMEBUFFER, &DstSize, pDstRect, cRects, fFlags); pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pDst->target, 0, 0); } void CrBltPresent(PCR_BLITTER pBlitter) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltPresent: blitter not entered")); return; } if (pBlitter->CtxInfo.Base.visualBits & CR_DOUBLE_BIT) pBlitter->pDispatch->SwapBuffers(pBlitter->CurrentMural.Base.id, 0); else pBlitter->pDispatch->Flush(); } static int crBltImgInitBaseForTex(const VBOXVR_TEXTURE *pSrc, CR_BLITTER_IMG *pDst, GLenum enmFormat) { memset(pDst, 0, sizeof (*pDst)); if (enmFormat != GL_RGBA && enmFormat != GL_BGRA) { WARN(("unsupported format 0x%x", enmFormat)); return VERR_NOT_IMPLEMENTED; } uint32_t bpp = 32; uint32_t pitch = ((bpp * pSrc->width) + 7) >> 3; uint32_t cbData = pitch * pSrc->height; pDst->cbData = cbData; pDst->enmFormat = enmFormat; pDst->width = pSrc->width; pDst->height = pSrc->height; pDst->bpp = bpp; pDst->pitch = pitch; return VINF_SUCCESS; } static int crBltImgCreateForTex(const VBOXVR_TEXTURE *pSrc, CR_BLITTER_IMG *pDst, GLenum enmFormat) { int rc = crBltImgInitBaseForTex(pSrc, pDst, enmFormat); if (!RT_SUCCESS(rc)) { crWarning("crBltImgInitBaseForTex failed rc %d", rc); return rc; } uint32_t cbData = pDst->cbData; pDst->pvData = RTMemAllocZ(cbData); if (!pDst->pvData) { crWarning("RTMemAlloc failed"); return VERR_NO_MEMORY; } #ifdef DEBUG_misha { char *pTmp = (char*)pDst->pvData; for (uint32_t i = 0; i < cbData; ++i) { pTmp[i] = (char)((1 << i) % 255); } } #endif return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrBltImgGetTex(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, GLenum enmFormat, CR_BLITTER_IMG *pDst) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltImgGetTex: blitter not entered")); return VERR_INVALID_STATE; } int rc = crBltImgCreateForTex(pSrc, pDst, enmFormat); if (!RT_SUCCESS(rc)) { crWarning("crBltImgCreateForTex failed, rc %d", rc); return rc; } pBlitter->pDispatch->BindTexture(pSrc->target, pSrc->hwid); #ifdef DEBUG_misha { GLint width = 0, height = 0, depth = 0; pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_WIDTH, &width); pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_HEIGHT, &height); pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_DEPTH, &depth); Assert(width == pSrc->width); Assert(height == pSrc->height); // Assert(depth == pSrc->depth); } #endif pBlitter->pDispatch->GetTexImage(pSrc->target, 0, enmFormat, GL_UNSIGNED_BYTE, pDst->pvData); pBlitter->pDispatch->BindTexture(pSrc->target, 0); return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrBltImgGetMural(PCR_BLITTER pBlitter, bool fBb, CR_BLITTER_IMG *pDst) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltImgGetMural: blitter not entered")); return VERR_INVALID_STATE; } WARN(("NOT IMPLEMENTED")); return VERR_NOT_IMPLEMENTED; } VBOXBLITTERDECL(void) CrBltImgFree(PCR_BLITTER pBlitter, CR_BLITTER_IMG *pDst) { if (!CrBltIsEntered(pBlitter)) { WARN(("CrBltImgFree: blitter not entered")); return; } if (pDst->pvData) { RTMemFree(pDst->pvData); pDst->pvData = NULL; } } VBOXBLITTERDECL(bool) CrGlslIsSupported(CR_GLSL_CACHE *pCache) { if (pCache->iGlVersion == 0) { const char * pszStr = (const char*)pCache->pDispatch->GetString(GL_VERSION); pCache->iGlVersion = crStrParseGlVersion(pszStr); if (pCache->iGlVersion <= 0) { crWarning("crStrParseGlVersion returned %d", pCache->iGlVersion); pCache->iGlVersion = -1; } } if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 0, 0)) return true; crWarning("GLSL unsuported, gl version %d", pCache->iGlVersion); /* @todo: we could also check for GL_ARB_shader_objects and GL_ARB_fragment_shader, * but seems like chromium does not support properly gl*Object versions of shader functions used with those extensions */ return false; } #define CR_GLSL_STR_V_120 "#version 120\n" #define CR_GLSL_STR_EXT_TR "#extension GL_ARB_texture_rectangle : enable\n" #define CR_GLSL_STR_2D "2D" #define CR_GLSL_STR_2DRECT "2DRect" #define CR_GLSL_PATTERN_FS_NOALPHA(_ver, _ext, _tex) \ _ver \ _ext \ "uniform sampler" _tex " sampler0;\n" \ "void main()\n" \ "{\n" \ "vec2 srcCoord = vec2(gl_TexCoord[0]);\n" \ "gl_FragData[0].xyz = (texture" _tex "(sampler0, srcCoord).xyz);\n" \ "gl_FragData[0].w = 1.0;\n" \ "}\n" static const char* crGlslGetFsStringNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { if (!CrGlslIsSupported(pCache)) { crWarning("CrGlslIsSupported is false"); return NULL; } if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 1, 0)) { if (enmTexTarget == GL_TEXTURE_2D) return CR_GLSL_PATTERN_FS_NOALPHA(CR_GLSL_STR_V_120, "", CR_GLSL_STR_2D); else if (enmTexTarget == GL_TEXTURE_RECTANGLE_ARB) return CR_GLSL_PATTERN_FS_NOALPHA(CR_GLSL_STR_V_120, CR_GLSL_STR_EXT_TR, CR_GLSL_STR_2DRECT); crWarning("invalid enmTexTarget %#x", enmTexTarget); return NULL; } else if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 0, 0)) { if (enmTexTarget == GL_TEXTURE_2D) return CR_GLSL_PATTERN_FS_NOALPHA("", "", CR_GLSL_STR_2D); else if (enmTexTarget == GL_TEXTURE_RECTANGLE_ARB) return CR_GLSL_PATTERN_FS_NOALPHA("", CR_GLSL_STR_EXT_TR, CR_GLSL_STR_2DRECT); crWarning("invalid enmTexTarget %#x", enmTexTarget); return NULL; } crError("crGlslGetFsStringNoAlpha: we should not be here!"); return NULL; } static int crGlslProgGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget, GLuint *puiProgram) { *puiProgram = 0; const char*pStrFsShader = crGlslGetFsStringNoAlpha(pCache, enmTexTarget); if (!pStrFsShader) { crWarning("crGlslGetFsStringNoAlpha failed"); return VERR_NOT_SUPPORTED; } int rc = VINF_SUCCESS; GLchar * pBuf = NULL; GLuint uiProgram = 0; GLint iUniform = -1; GLuint uiShader = pCache->pDispatch->CreateShader(GL_FRAGMENT_SHADER); if (!uiShader) { crWarning("CreateShader failed"); return VERR_NOT_SUPPORTED; } pCache->pDispatch->ShaderSource(uiShader, 1, &pStrFsShader, NULL); pCache->pDispatch->CompileShader(uiShader); GLint compiled = 0; pCache->pDispatch->GetShaderiv(uiShader, GL_COMPILE_STATUS, &compiled); #ifndef DEBUG_misha if(!compiled) #endif { if (!pBuf) pBuf = (GLchar *)RTMemAlloc(16300); pCache->pDispatch->GetShaderInfoLog(uiShader, 16300, NULL, pBuf); #ifdef DEBUG_misha if (compiled) crDebug("compile success:\n-------------------\n%s\n--------\n", pBuf); else #endif { crWarning("compile FAILURE:\n-------------------\n%s\n--------\n", pBuf); rc = VERR_NOT_SUPPORTED; goto end; } } Assert(compiled); uiProgram = pCache->pDispatch->CreateProgram(); if (!uiProgram) { rc = VERR_NOT_SUPPORTED; goto end; } pCache->pDispatch->AttachShader(uiProgram, uiShader); pCache->pDispatch->LinkProgram(uiProgram); GLint linked; pCache->pDispatch->GetProgramiv(uiProgram, GL_LINK_STATUS, &linked); #ifndef DEBUG_misha if(!linked) #endif { if (!pBuf) pBuf = (GLchar *)RTMemAlloc(16300); pCache->pDispatch->GetProgramInfoLog(uiProgram, 16300, NULL, pBuf); #ifdef DEBUG_misha if (linked) crDebug("link success:\n-------------------\n%s\n--------\n", pBuf); else #endif { crWarning("link FAILURE:\n-------------------\n%s\n--------\n", pBuf); rc = VERR_NOT_SUPPORTED; goto end; } } Assert(linked); iUniform = pCache->pDispatch->GetUniformLocation(uiProgram, "sampler0"); if (iUniform == -1) { crWarning("GetUniformLocation failed for sampler0"); } else { pCache->pDispatch->Uniform1i(iUniform, 0); } *puiProgram = uiProgram; /* avoid end finalizer from cleaning it */ uiProgram = 0; end: if (uiShader) pCache->pDispatch->DeleteShader(uiShader); if (uiProgram) pCache->pDispatch->DeleteProgram(uiProgram); if (pBuf) RTMemFree(pBuf); return rc; } DECLINLINE(GLuint) crGlslProgGetNoAlpha(const CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { switch (enmTexTarget) { case GL_TEXTURE_2D: return pCache->uNoAlpha2DProg; case GL_TEXTURE_RECTANGLE_ARB: return pCache->uNoAlpha2DRectProg; default: crWarning("invalid tex enmTexTarget %#x", enmTexTarget); return 0; } } DECLINLINE(GLuint*) crGlslProgGetNoAlphaPtr(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { switch (enmTexTarget) { case GL_TEXTURE_2D: return &pCache->uNoAlpha2DProg; case GL_TEXTURE_RECTANGLE_ARB: return &pCache->uNoAlpha2DRectProg; default: crWarning("invalid tex enmTexTarget %#x", enmTexTarget); return NULL; } } VBOXBLITTERDECL(int) CrGlslProgGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { GLuint*puiProgram = crGlslProgGetNoAlphaPtr(pCache, enmTexTarget); if (!puiProgram) return VERR_INVALID_PARAMETER; if (*puiProgram) return VINF_SUCCESS; return crGlslProgGenNoAlpha(pCache, enmTexTarget, puiProgram); } VBOXBLITTERDECL(int) CrGlslProgGenAllNoAlpha(CR_GLSL_CACHE *pCache) { int rc = CrGlslProgGenNoAlpha(pCache, GL_TEXTURE_2D); if (!RT_SUCCESS(rc)) { crWarning("CrGlslProgGenNoAlpha GL_TEXTURE_2D failed rc %d", rc); return rc; } rc = CrGlslProgGenNoAlpha(pCache, GL_TEXTURE_RECTANGLE_ARB); if (!RT_SUCCESS(rc)) { crWarning("CrGlslProgGenNoAlpha GL_TEXTURE_RECTANGLE failed rc %d", rc); return rc; } return VINF_SUCCESS; } VBOXBLITTERDECL(void) CrGlslProgClear(const CR_GLSL_CACHE *pCache) { pCache->pDispatch->UseProgram(0); } VBOXBLITTERDECL(int) CrGlslProgUseNoAlpha(const CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { GLuint uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); if (!uiProg) { crWarning("request to use inexistent program!"); return VERR_INVALID_STATE; } Assert(uiProg); pCache->pDispatch->UseProgram(uiProg); return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrGlslProgUseGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) { GLuint uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); if (!uiProg) { int rc = CrGlslProgGenNoAlpha(pCache, enmTexTarget); if (!RT_SUCCESS(rc)) { crWarning("CrGlslProgGenNoAlpha failed, rc %d", rc); return rc; } uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); CRASSERT(uiProg); } Assert(uiProg); pCache->pDispatch->UseProgram(uiProg); return VINF_SUCCESS; } VBOXBLITTERDECL(bool) CrGlslNeedsCleanup(const CR_GLSL_CACHE *pCache) { return pCache->uNoAlpha2DProg || pCache->uNoAlpha2DRectProg; } VBOXBLITTERDECL(void) CrGlslCleanup(CR_GLSL_CACHE *pCache) { if (pCache->uNoAlpha2DProg) { pCache->pDispatch->DeleteProgram(pCache->uNoAlpha2DProg); pCache->uNoAlpha2DProg = 0; } if (pCache->uNoAlpha2DRectProg) { pCache->pDispatch->DeleteProgram(pCache->uNoAlpha2DRectProg); pCache->uNoAlpha2DRectProg = 0; } } VBOXBLITTERDECL(void) CrGlslTerm(CR_GLSL_CACHE *pCache) { CRASSERT(!CrGlslNeedsCleanup(pCache)); CrGlslCleanup(pCache); /* sanity */ memset(pCache, 0, sizeof (*pCache)); } /*TdBlt*/ static void crTdBltCheckPBO(PCR_TEXDATA pTex) { if (pTex->idPBO) return; PCR_BLITTER pBlitter = pTex->pBlitter; if (!pBlitter->Flags.SupportsPBO) return; pBlitter->pDispatch->GenBuffersARB(1, &pTex->idPBO); if (!pTex->idPBO) { crWarning("PBO create failed"); return; } pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); pBlitter->pDispatch->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->Tex.width*pTex->Tex.height*4, 0, GL_STREAM_READ_ARB); pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); } static uint32_t crTdBltTexCreate(PCR_BLITTER pBlitter, uint32_t width, uint32_t height, GLenum enmTarget) { uint32_t tex = 0; pBlitter->pDispatch->GenTextures(1, &tex); if (!tex) { crWarning("Tex create failed"); return 0; } pBlitter->pDispatch->BindTexture(enmTarget, tex); pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_WRAP_S, GL_CLAMP); pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_WRAP_T, GL_CLAMP); pBlitter->pDispatch->TexImage2D(enmTarget, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); /*Restore gl state*/ pBlitter->pDispatch->BindTexture(enmTarget, 0); return tex; } int crTdBltCheckInvertTex(PCR_TEXDATA pTex) { if (pTex->idInvertTex) return VINF_SUCCESS; pTex->idInvertTex = crTdBltTexCreate(pTex->pBlitter, pTex->Tex.width, pTex->Tex.height, pTex->Tex.target); if (!pTex->idInvertTex) { crWarning("Invert Tex create failed"); return VERR_GENERAL_FAILURE; } return VINF_SUCCESS; } void crTdBltImgRelease(PCR_TEXDATA pTex) { pTex->Flags.DataValid = 0; } void crTdBltImgFree(PCR_TEXDATA pTex) { if (!pTex->Img.pvData) { Assert(!pTex->Flags.DataValid); return; } crTdBltImgRelease(pTex); Assert(!pTex->Flags.DataValid); if (pTex->idPBO) { PCR_BLITTER pBlitter = pTex->pBlitter; Assert(CrBltIsEntered(pBlitter)); pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); pBlitter->pDispatch->UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); } else { Assert(pTex->Img.pvData); RTMemFree(pTex->Img.pvData); } pTex->Img.pvData = NULL; } int crTdBltImgAcquire(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted) { void *pvData = pTex->Img.pvData; Assert(!pTex->Flags.DataValid); int rc = crBltImgInitBaseForTex(&pTex->Tex, &pTex->Img, enmFormat); if (!RT_SUCCESS(rc)) { WARN(("crBltImgInitBaseForTex failed rc %d", rc)); return rc; } PCR_BLITTER pBlitter = pTex->pBlitter; Assert(CrBltIsEntered(pBlitter)); pBlitter->pDispatch->BindTexture(pTex->Tex.target, fInverted ? pTex->idInvertTex : pTex->Tex.hwid); pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); if (pvData) { if (pTex->idPBO) { pBlitter->pDispatch->UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); pvData = NULL; } } else { if (!pTex->idPBO) { pvData = RTMemAlloc(4*pTex->Tex.width*pTex->Tex.height); if (!pvData) { WARN(("Out of memory in crTdBltImgAcquire")); pBlitter->pDispatch->BindTexture(pTex->Tex.target, 0); return VERR_NO_MEMORY; } } } Assert(!pvData == !!pTex->idPBO); /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/ pBlitter->pDispatch->GetTexImage(GL_TEXTURE_2D, 0, enmFormat, GL_UNSIGNED_BYTE, pvData); /*restore gl state*/ pBlitter->pDispatch->BindTexture(pTex->Tex.target, 0); if (pTex->idPBO) { pvData = pBlitter->pDispatch->MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); if (!pvData) { WARN(("Failed to MapBuffer in CrHlpGetTexImage")); return VERR_GENERAL_FAILURE; } pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); } Assert(pvData); pTex->Img.pvData = pvData; pTex->Flags.DataValid = 1; pTex->Flags.DataInverted = fInverted; return VINF_SUCCESS; } /* release the texture data, the data remains cached in the CR_TEXDATA object until it is discarded with CrTdBltDataInvalidateNe or CrTdBltDataCleanup */ VBOXBLITTERDECL(int) CrTdBltDataRelease(PCR_TEXDATA pTex) { if (!pTex->Flags.Entered) { WARN(("tex not entered")); return VERR_INVALID_STATE; } if (!pTex->Flags.DataAcquired) { WARN(("Data NOT acquired")); return VERR_INVALID_STATE; } Assert(pTex->Img.pvData); Assert(pTex->Flags.DataValid); pTex->Flags.DataAcquired = 0; return VINF_SUCCESS; } static void crTdBltDataFree(PCR_TEXDATA pTex) { crTdBltImgFree(pTex); if (pTex->pScaledCache) CrTdBltDataFreeNe(pTex->pScaledCache); } /* discard the texture data cached with previous CrTdBltDataAcquire. * Must be called wit data released (CrTdBltDataRelease) */ VBOXBLITTERDECL(int) CrTdBltDataFree(PCR_TEXDATA pTex) { if (!pTex->Flags.Entered) { WARN(("tex not entered")); return VERR_INVALID_STATE; } crTdBltDataFree(pTex); return VINF_SUCCESS; } VBOXBLITTERDECL(void) CrTdBltDataInvalidateNe(PCR_TEXDATA pTex) { crTdBltImgRelease(pTex); if (pTex->pScaledCache) CrTdBltDataInvalidateNe(pTex->pScaledCache); } VBOXBLITTERDECL(int) CrTdBltDataFreeNe(PCR_TEXDATA pTex) { if (!pTex->Img.pvData) return VINF_SUCCESS; bool fEntered = false; if (pTex->idPBO) { int rc = CrTdBltEnter(pTex); if (!RT_SUCCESS(rc)) { WARN(("err")); return rc; } fEntered = true; } crTdBltDataFree(pTex); if (fEntered) CrTdBltLeave(pTex); return VINF_SUCCESS; } static void crTdBltSdCleanupCacheNe(PCR_TEXDATA pTex) { if (pTex->pScaledCache) { CrTdBltDataCleanupNe(pTex->pScaledCache); CrTdRelease(pTex->pScaledCache); pTex->pScaledCache = NULL; } } static void crTdBltDataCleanup(PCR_TEXDATA pTex) { crTdBltImgFree(pTex); PCR_BLITTER pBlitter = pTex->pBlitter; if (pTex->idPBO) { Assert(CrBltIsEntered(pBlitter)); pBlitter->pDispatch->DeleteBuffersARB(1, &pTex->idPBO); pTex->idPBO = 0; } if (pTex->idInvertTex) { Assert(CrBltIsEntered(pBlitter)); pBlitter->pDispatch->DeleteTextures(1, &pTex->idInvertTex); pTex->idInvertTex = 0; } crTdBltSdCleanupCacheNe(pTex); } /* does same as CrTdBltDataFree, and in addition cleans up */ VBOXBLITTERDECL(int) CrTdBltDataCleanup(PCR_TEXDATA pTex) { if (!pTex->Flags.Entered) { WARN(("tex not entered")); return VERR_INVALID_STATE; } crTdBltDataCleanup(pTex); return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrTdBltDataCleanupNe(PCR_TEXDATA pTex) { bool fEntered = false; if (pTex->idPBO || pTex->idInvertTex) { int rc = CrTdBltEnter(pTex); if (!RT_SUCCESS(rc)) { WARN(("err")); return rc; } fEntered = true; } crTdBltDataCleanup(pTex); if (fEntered) CrTdBltLeave(pTex); return VINF_SUCCESS; } /* acquire the texture data, returns the cached data in case it is cached. * the data remains cached in the CR_TEXDATA object until it is discarded with CrTdBltDataFree or CrTdBltDataCleanup. * */ VBOXBLITTERDECL(int) CrTdBltDataAcquire(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted, const CR_BLITTER_IMG**ppImg) { if (!pTex->Flags.Entered) { WARN(("tex not entered")); return VERR_INVALID_STATE; } if (pTex->Flags.DataAcquired) { WARN(("Data acquired already")); return VERR_INVALID_STATE; } if (pTex->Flags.DataValid && pTex->Img.enmFormat == enmFormat && !pTex->Flags.DataInverted == !fInverted) { Assert(pTex->Img.pvData); *ppImg = &pTex->Img; pTex->Flags.DataAcquired = 1; return VINF_SUCCESS; } crTdBltImgRelease(pTex); crTdBltCheckPBO(pTex); int rc; if (fInverted) { rc = crTdBltCheckInvertTex(pTex); if (!RT_SUCCESS(rc)) { WARN(("crTdBltCheckInvertTex failed rc %d", rc)); return rc; } RTRECT SrcRect, DstRect; VBOXVR_TEXTURE InvertTex; InvertTex = pTex->Tex; InvertTex.hwid = pTex->idInvertTex; SrcRect.xLeft = 0; SrcRect.yTop = InvertTex.height; SrcRect.xRight = InvertTex.width; SrcRect.yBottom = 0; DstRect.xLeft = 0; DstRect.yTop = 0; DstRect.xRight = InvertTex.width; DstRect.yBottom = InvertTex.height; CrBltBlitTexTex(pTex->pBlitter, &pTex->Tex, &SrcRect, &InvertTex, &DstRect, 1, 0); } rc = crTdBltImgAcquire(pTex, enmFormat, fInverted); if (!RT_SUCCESS(rc)) { WARN(("crTdBltImgAcquire failed rc %d", rc)); return rc; } Assert(pTex->Img.pvData); *ppImg = &pTex->Img; pTex->Flags.DataAcquired = 1; return VINF_SUCCESS; } DECLINLINE(void) crTdResize(PCR_TEXDATA pTex, const VBOXVR_TEXTURE *pVrTex) { crTdBltDataCleanup(pTex); pTex->Tex = *pVrTex; } static DECLCALLBACK(void) ctTdBltSdReleased(struct CR_TEXDATA *pTexture) { PCR_BLITTER pBlitter = pTexture->pBlitter; int rc = CrBltEnter(pBlitter); if (!RT_SUCCESS(rc)) { WARN(("CrBltEnter failed, rc %d", rc)); return; } CrTdBltDataCleanupNe(pTexture); pBlitter->pDispatch->DeleteTextures(1, &pTexture->Tex.hwid); CrBltLeave(pBlitter); RTMemFree(pTexture); } static int ctTdBltSdCreate(PCR_BLITTER pBlitter, uint32_t width, uint32_t height, GLenum enmTarget, PCR_TEXDATA *ppScaledCache) { PCR_TEXDATA pScaledCache; Assert(CrBltIsEntered(pBlitter)); *ppScaledCache = NULL; pScaledCache = (PCR_TEXDATA)RTMemAlloc(sizeof (*pScaledCache)); if (!pScaledCache) { WARN(("RTMemAlloc failed")); return VERR_NO_MEMORY; } VBOXVR_TEXTURE Tex; Tex.width = width; Tex.height = height; Tex.target = enmTarget; Tex.hwid = crTdBltTexCreate(pBlitter, width, height, enmTarget); if (!Tex.hwid) { WARN(("Tex create failed")); RTMemFree(pScaledCache); return VERR_GENERAL_FAILURE; } CrTdInit(pScaledCache, &Tex, pBlitter, ctTdBltSdReleased); *ppScaledCache = pScaledCache; return VINF_SUCCESS; } static int ctTdBltSdGet(PCR_TEXDATA pTex, uint32_t width, uint32_t height, PCR_TEXDATA *ppScaledCache) { Assert(CrBltIsEntered(pTex->pBlitter)); PCR_TEXDATA pScaledCache; *ppScaledCache = NULL; if (!pTex->pScaledCache) { int rc = ctTdBltSdCreate(pTex->pBlitter, width, height, pTex->Tex.target, &pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("ctTdBltSdCreate failed %d", rc)); return rc; } pTex->pScaledCache = pScaledCache; } else { int cmp = pTex->pScaledCache->Tex.width - width; if (cmp <= 0) cmp = pTex->pScaledCache->Tex.height - height; if (!cmp) pScaledCache = pTex->pScaledCache; else if (cmp < 0) /* current cache is "less" than the requested */ { int rc = ctTdBltSdCreate(pTex->pBlitter, width, height, pTex->Tex.target, &pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("ctTdBltSdCreate failed %d", rc)); return rc; } pScaledCache->pScaledCache = pTex->pScaledCache; pTex->pScaledCache = pScaledCache; } else /* cmp > 0 */ { int rc = ctTdBltSdGet(pTex->pScaledCache, width, height, &pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("ctTdBltSdGet failed %d", rc)); return rc; } } } Assert(pScaledCache); #if 0 { VBOXVR_TEXTURE Tex; Tex.width = width; Tex.height = height; Tex.target = pTex->Tex.target; Tex.hwid = crTdBltTexCreate(pTex, width, height); if (!Tex.hwid) { WARN(("Tex create failed")); return VERR_GENERAL_FAILURE; } pTex->pBlitter->pDispatch->DeleteTextures(1, &pTex->pScaledCache->Tex.hwid); crTdResize(pTex->pScaledCache, &Tex); } #endif *ppScaledCache = pScaledCache; return VINF_SUCCESS; } static int ctTdBltSdGetUpdated(PCR_TEXDATA pTex, uint32_t width, uint32_t height, PCR_TEXDATA *ppScaledCache) { PCR_TEXDATA pScaledCache; *ppScaledCache = NULL; int rc = ctTdBltSdGet(pTex, width, height, &pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("ctTdBltSdGet failed %d", rc)); return rc; } Assert(width == pScaledCache->Tex.width); Assert(height == pScaledCache->Tex.height); if (!pScaledCache->Flags.DataValid) { RTRECT SrcRect, DstRect; SrcRect.xLeft = 0; SrcRect.yTop = 0; SrcRect.xRight = pTex->Tex.width; SrcRect.yBottom = pTex->Tex.height; DstRect.xLeft = 0; DstRect.yTop = 0; DstRect.xRight = width; DstRect.yBottom = height; CrBltBlitTexTex(pTex->pBlitter, &pTex->Tex, &SrcRect, &pScaledCache->Tex, &DstRect, 1, 0); } *ppScaledCache = pScaledCache; return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrTdBltDataAcquireScaled(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted, uint32_t width, uint32_t height, const CR_BLITTER_IMG**ppImg) { if (pTex->Tex.width == width && pTex->Tex.height == height) return CrTdBltDataAcquire(pTex, enmFormat, fInverted, ppImg); if (!pTex->Flags.Entered) { WARN(("tex not entered")); return VERR_INVALID_STATE; } PCR_TEXDATA pScaledCache; int rc = ctTdBltSdGetUpdated(pTex, width, height, &pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("ctTdBltSdGetUpdated failed rc %d", rc)); return rc; } rc = CrTdBltEnter(pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("CrTdBltEnter failed rc %d", rc)); return rc; } rc = CrTdBltDataAcquire(pScaledCache, enmFormat, fInverted, ppImg); if (!RT_SUCCESS(rc)) { WARN(("CrTdBltDataAcquire failed rc %d", rc)); CrTdBltLeave(pTex->pScaledCache); return rc; } return VINF_SUCCESS; } VBOXBLITTERDECL(int) CrTdBltDataReleaseScaled(PCR_TEXDATA pTex, const CR_BLITTER_IMG *pImg) { PCR_TEXDATA pScaledCache = RT_FROM_MEMBER(pImg, CR_TEXDATA, Img); int rc = CrTdBltDataRelease(pScaledCache); if (!RT_SUCCESS(rc)) { WARN(("CrTdBltDataRelease failed rc %d", rc)); return rc; } if (pScaledCache != pTex) CrTdBltLeave(pScaledCache); return VINF_SUCCESS; } VBOXBLITTERDECL(void) CrTdBltScaleCacheMoveTo(PCR_TEXDATA pTex, PCR_TEXDATA pDstTex) { if (!pTex->pScaledCache) return; crTdBltSdCleanupCacheNe(pDstTex); pDstTex->pScaledCache = pTex->pScaledCache; pTex->pScaledCache = NULL; }