summaryrefslogtreecommitdiffstats
path: root/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c')
-rw-r--r--src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c830
1 files changed, 830 insertions, 0 deletions
diff --git a/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c
new file mode 100644
index 0000000000..8dc0120fe1
--- /dev/null
+++ b/src/plugins/gfxdrivers/powervr/QWSWSEGL/pvrqwsdrawable.c
@@ -0,0 +1,830 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "pvrqwsdrawable_p.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+PvrQwsDisplay pvrQwsDisplay;
+
+static void pvrQwsDestroyDrawableForced(PvrQwsDrawable *drawable);
+
+/* Initialize the /dev/fbN device for a specific screen */
+static int pvrQwsInitFbScreen(int screen)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ unsigned long start;
+ unsigned long length;
+ int width, height, stride;
+ PVR2DFORMAT format;
+ void *mapped;
+ int fd, bytesPerPixel;
+ char name[64];
+ PVR2DMEMINFO *memInfo;
+ unsigned long pageAddresses[2];
+
+ /* Bail out if already initialized, or the number is incorrect */
+ if (screen < 0 || screen >= PVRQWS_MAX_SCREENS)
+ return 0;
+ if (pvrQwsDisplay.screens[screen].initialized)
+ return 1;
+
+ /* Open the framebuffer and fetch its properties */
+ sprintf(name, "/dev/fb%d", screen);
+ fd = open(name, O_RDWR, 0);
+ if (fd < 0) {
+ perror(name);
+ return 0;
+ }
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) {
+ perror("FBIOGET_VSCREENINFO");
+ close(fd);
+ return 0;
+ }
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) < 0) {
+ perror("FBIOGET_FSCREENINFO");
+ close(fd);
+ return 0;
+ }
+ width = var.xres;
+ height = var.yres;
+ bytesPerPixel = var.bits_per_pixel / 8;
+ stride = fix.line_length;
+ format = PVR2D_1BPP;
+ if (var.bits_per_pixel == 16) {
+ if (var.red.length == 5 && var.green.length == 6 &&
+ var.blue.length == 5 && var.red.offset == 11 &&
+ var.green.offset == 5 && var.blue.offset == 0) {
+ format = PVR2D_RGB565;
+ }
+ if (var.red.length == 4 && var.green.length == 4 &&
+ var.blue.length == 4 && var.transp.length == 4 &&
+ var.red.offset == 8 && var.green.offset == 4 &&
+ var.blue.offset == 0 && var.transp.offset == 12) {
+ format = PVR2D_ARGB4444;
+ }
+ } else if (var.bits_per_pixel == 32) {
+ if (var.red.length == 8 && var.green.length == 8 &&
+ var.blue.length == 8 && var.transp.length == 8 &&
+ var.red.offset == 16 && var.green.offset == 8 &&
+ var.blue.offset == 0 && var.transp.offset == 24) {
+ format = PVR2D_ARGB8888;
+ }
+ }
+ if (format == PVR2D_1BPP) {
+ fprintf(stderr, "%s: could not find a suitable PVR2D pixel format\n", name);
+ close(fd);
+ return 0;
+ }
+ start = fix.smem_start;
+ length = var.xres_virtual * var.yres_virtual * bytesPerPixel;
+
+ if (screen == 0) {
+ /* We use PVR2DGetFrameBuffer to map the first screen.
+ On some chipsets it is more reliable than using PVR2DMemWrap */
+ mapped = 0;
+ memInfo = 0;
+ } else {
+ /* Other screens: map the framebuffer region into memory */
+ mapped = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (!mapped || mapped == (void *)(-1)) {
+ perror("mmap");
+ close(fd);
+ return 0;
+ }
+
+ /* Allocate a PVR2D memory region for the framebuffer */
+ memInfo = 0;
+ if (pvrQwsDisplay.context) {
+ pageAddresses[0] = start & 0xFFFFF000;
+ pageAddresses[1] = 0;
+ if (PVR2DMemWrap
+ (pvrQwsDisplay.context, mapped, PVR2D_WRAPFLAG_CONTIGUOUS,
+ length, pageAddresses, &memInfo) != PVR2D_OK) {
+ munmap(mapped, length);
+ close(fd);
+ return 0;
+ }
+ }
+ }
+
+ /* We don't need the file descriptor any more */
+ close(fd);
+
+ /* The framebuffer is ready, so initialize the PvrQwsScreenInfo */
+ pvrQwsDisplay.screens[screen].screenRect.x = 0;
+ pvrQwsDisplay.screens[screen].screenRect.y = 0;
+ pvrQwsDisplay.screens[screen].screenRect.width = width;
+ pvrQwsDisplay.screens[screen].screenRect.height = height;
+ pvrQwsDisplay.screens[screen].screenStride = stride;
+ pvrQwsDisplay.screens[screen].pixelFormat = format;
+ pvrQwsDisplay.screens[screen].bytesPerPixel = bytesPerPixel;
+ pvrQwsDisplay.screens[screen].screenDrawable = 0;
+ if (mapped) {
+ /* Don't set these fields if mapped is 0, because PVR2DGetFrameBuffer
+ may have already been called and set them */
+ pvrQwsDisplay.screens[screen].frameBuffer = memInfo;
+ pvrQwsDisplay.screens[screen].mapped = mapped;
+ }
+ pvrQwsDisplay.screens[screen].mappedLength = length;
+ pvrQwsDisplay.screens[screen].screenStart = start;
+ pvrQwsDisplay.screens[screen].needsUnmap = (mapped != 0);
+ pvrQwsDisplay.screens[screen].initialized = 1;
+ return 1;
+}
+
+/* Called when a new drawable is added to ensure that we have a
+ PVR2D context and framebuffer PVR2DMEMINFO blocks */
+static int pvrQwsAddDrawable(void)
+{
+ int numDevs, screen;
+ PVR2DDEVICEINFO *devs;
+ unsigned long devId;
+ unsigned long pageAddresses[2];
+ PVR2DMEMINFO *memInfo;
+ PVR2DDISPLAYINFO displayInfo;
+
+ /* Bail out early if this is not the first drawable */
+ if (pvrQwsDisplay.numDrawables > 0) {
+ ++(pvrQwsDisplay.numDrawables);
+ return 1;
+ }
+
+ /* Find the first PVR2D device in the system and open it */
+ numDevs = PVR2DEnumerateDevices(0);
+ if (numDevs <= 0)
+ return 0;
+ devs = (PVR2DDEVICEINFO *)malloc(sizeof(PVR2DDEVICEINFO) * numDevs);
+ if (!devs)
+ return 0;
+ if (PVR2DEnumerateDevices(devs) != PVR2D_OK) {
+ free(devs);
+ return 0;
+ }
+ devId = devs[0].ulDevID;
+ free(devs);
+ if (PVR2DCreateDeviceContext(devId, &pvrQwsDisplay.context, 0) != PVR2D_OK)
+ return 0;
+ pvrQwsDisplay.numFlipBuffers = 0;
+ pvrQwsDisplay.flipChain = 0;
+ if (PVR2DGetDeviceInfo(pvrQwsDisplay.context, &displayInfo) == PVR2D_OK) {
+ if (displayInfo.ulMaxFlipChains > 0 && displayInfo.ulMaxBuffersInChain > 0)
+ pvrQwsDisplay.numFlipBuffers = displayInfo.ulMaxBuffersInChain;
+ if (pvrQwsDisplay.numFlipBuffers > PVRQWS_MAX_FLIP_BUFFERS)
+ pvrQwsDisplay.numFlipBuffers = PVRQWS_MAX_FLIP_BUFFERS;
+ }
+
+ /* Create the PVR2DMEMINFO blocks for the active framebuffers */
+ for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) {
+ if (screen != 0 && pvrQwsDisplay.screens[screen].mapped) {
+ pageAddresses[0]
+ = pvrQwsDisplay.screens[screen].screenStart & 0xFFFFF000;
+ pageAddresses[1] = 0;
+ if (PVR2DMemWrap
+ (pvrQwsDisplay.context,
+ pvrQwsDisplay.screens[screen].mapped,
+ PVR2D_WRAPFLAG_CONTIGUOUS,
+ pvrQwsDisplay.screens[screen].mappedLength,
+ pageAddresses, &memInfo) != PVR2D_OK) {
+ PVR2DDestroyDeviceContext(pvrQwsDisplay.context);
+ pvrQwsDisplay.context = 0;
+ return 0;
+ }
+ pvrQwsDisplay.screens[screen].frameBuffer = memInfo;
+ } else if (screen == 0) {
+ if (PVR2DGetFrameBuffer
+ (pvrQwsDisplay.context,
+ PVR2D_FB_PRIMARY_SURFACE, &memInfo) != PVR2D_OK) {
+ fprintf(stderr, "QWSWSEGL: could not get the primary framebuffer surface\n");
+ PVR2DDestroyDeviceContext(pvrQwsDisplay.context);
+ pvrQwsDisplay.context = 0;
+ return 0;
+ }
+ pvrQwsDisplay.screens[screen].frameBuffer = memInfo;
+ pvrQwsDisplay.screens[screen].mapped = memInfo->pBase;
+ }
+ }
+
+ /* Create a flip chain for the screen if supported by the hardware */
+ pvrQwsDisplay.usePresentBlit = 0;
+ if (pvrQwsDisplay.numFlipBuffers > 0) {
+ long stride = 0;
+ unsigned long flipId = 0;
+ unsigned long numBuffers;
+ if (PVR2DCreateFlipChain(pvrQwsDisplay.context, 0,
+ //PVR2D_CREATE_FLIPCHAIN_SHARED |
+ //PVR2D_CREATE_FLIPCHAIN_QUERY,
+ pvrQwsDisplay.numFlipBuffers,
+ pvrQwsDisplay.screens[0].screenRect.width,
+ pvrQwsDisplay.screens[0].screenRect.height,
+ pvrQwsDisplay.screens[0].pixelFormat,
+ &stride, &flipId, &(pvrQwsDisplay.flipChain))
+ == PVR2D_OK) {
+ pvrQwsDisplay.screens[0].screenStride = stride;
+ PVR2DGetFlipChainBuffers(pvrQwsDisplay.context,
+ pvrQwsDisplay.flipChain,
+ &numBuffers,
+ pvrQwsDisplay.flipBuffers);
+ } else {
+ pvrQwsDisplay.flipChain = 0;
+ pvrQwsDisplay.numFlipBuffers = 0;
+ }
+
+ /* PVR2DPresentBlt is a little more reliable than PVR2DBlt
+ when flip chains are present, even if we cannot create a
+ flip chain at the moment */
+ pvrQwsDisplay.usePresentBlit = 1;
+ }
+
+ /* The context is ready to go */
+ ++(pvrQwsDisplay.numDrawables);
+ return 1;
+}
+
+/* Called when the last drawable is destroyed. The PVR2D context
+ will be destroyed but the raw framebuffer memory will stay mapped */
+static void pvrQwsDestroyContext(void)
+{
+ int screen;
+ for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) {
+ if (pvrQwsDisplay.screens[screen].frameBuffer) {
+ PVR2DMemFree
+ (pvrQwsDisplay.context,
+ pvrQwsDisplay.screens[screen].frameBuffer);
+ pvrQwsDisplay.screens[screen].frameBuffer = 0;
+ }
+ }
+
+ if (pvrQwsDisplay.numFlipBuffers > 0)
+ PVR2DDestroyFlipChain(pvrQwsDisplay.context, pvrQwsDisplay.flipChain);
+ PVR2DDestroyDeviceContext(pvrQwsDisplay.context);
+ pvrQwsDisplay.context = 0;
+ pvrQwsDisplay.flipChain = 0;
+ pvrQwsDisplay.numFlipBuffers = 0;
+ pvrQwsDisplay.usePresentBlit = 0;
+}
+
+int pvrQwsDisplayOpen(void)
+{
+ int screen;
+
+ /* If the display is already open, increase reference count and return */
+ if (pvrQwsDisplay.refCount > 0) {
+ ++(pvrQwsDisplay.refCount);
+ return 1;
+ }
+
+ /* Open the framebuffer and map it directly */
+ if (!pvrQwsInitFbScreen(0)) {
+ --(pvrQwsDisplay.refCount);
+ return 0;
+ }
+
+ /* Clear the other screens. We will create them if they are referenced */
+ for (screen = 1; screen < PVRQWS_MAX_SCREENS; ++screen)
+ memset(&(pvrQwsDisplay.screens[screen]), 0, sizeof(PvrQwsScreenInfo));
+
+ /* The display is open and ready */
+ ++(pvrQwsDisplay.refCount);
+ return 1;
+}
+
+void pvrQwsDisplayClose(void)
+{
+ int screen;
+
+ if (pvrQwsDisplay.refCount == 0)
+ return;
+ if (--(pvrQwsDisplay.refCount) > 0)
+ return;
+
+ /* Prevent pvrQwsDestroyContext from being called for the time being */
+ ++pvrQwsDisplay.numDrawables;
+
+ /* Free the screens */
+ for (screen = 0; screen < PVRQWS_MAX_SCREENS; ++screen) {
+ PvrQwsScreenInfo *info = &(pvrQwsDisplay.screens[screen]);
+ if (info->screenDrawable)
+ pvrQwsDestroyDrawableForced(info->screenDrawable);
+ if (info->frameBuffer)
+ PVR2DMemFree(pvrQwsDisplay.context, info->frameBuffer);
+ if (info->mapped && info->needsUnmap)
+ munmap(info->mapped, info->mappedLength);
+ }
+
+ /* Now it is safe to destroy the PVR2D context */
+ --pvrQwsDisplay.numDrawables;
+ if (pvrQwsDisplay.context)
+ PVR2DDestroyDeviceContext(pvrQwsDisplay.context);
+
+ memset(&pvrQwsDisplay, 0, sizeof(pvrQwsDisplay));
+}
+
+int pvrQwsDisplayIsOpen(void)
+{
+ return (pvrQwsDisplay.refCount > 0);
+}
+
+/* Ensure that a specific screen has been initialized */
+static int pvrQwsEnsureScreen(int screen)
+{
+ if (screen < 0 || screen >= PVRQWS_MAX_SCREENS)
+ return 0;
+ if (!screen)
+ return 1;
+ return pvrQwsInitFbScreen(screen);
+}
+
+PvrQwsDrawable *pvrQwsScreenWindow(int screen)
+{
+ PvrQwsDrawable *drawable;
+
+ if (!pvrQwsEnsureScreen(screen))
+ return 0;
+
+ drawable = pvrQwsDisplay.screens[screen].screenDrawable;
+ if (drawable)
+ return drawable;
+
+ drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable));
+ if (!drawable)
+ return 0;
+
+ drawable->type = PvrQwsScreen;
+ drawable->screen = screen;
+ drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat;
+ drawable->rect = pvrQwsDisplay.screens[screen].screenRect;
+ drawable->visibleRects[0] = drawable->rect;
+ drawable->numVisibleRects = 1;
+ drawable->isFullScreen = 1;
+
+ if (!pvrQwsAddDrawable()) {
+ free(drawable);
+ return 0;
+ }
+
+ pvrQwsDisplay.screens[screen].screenDrawable = drawable;
+
+ return drawable;
+}
+
+PvrQwsDrawable *pvrQwsCreateWindow(int screen, long winId, const PvrQwsRect *rect)
+{
+ PvrQwsDrawable *drawable;
+
+ if (!pvrQwsEnsureScreen(screen))
+ return 0;
+
+ drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable));
+ if (!drawable)
+ return 0;
+
+ drawable->type = PvrQwsWindow;
+ drawable->winId = winId;
+ drawable->refCount = 1;
+ drawable->screen = screen;
+ drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat;
+ drawable->rect = *rect;
+
+ if (!pvrQwsAddDrawable()) {
+ free(drawable);
+ return 0;
+ }
+
+ drawable->nextWinId = pvrQwsDisplay.firstWinId;
+ pvrQwsDisplay.firstWinId = drawable;
+
+ return drawable;
+}
+
+PvrQwsDrawable *pvrQwsFetchWindow(long winId)
+{
+ PvrQwsDrawable *drawable = pvrQwsDisplay.firstWinId;
+ while (drawable != 0 && drawable->winId != winId)
+ drawable = drawable->nextWinId;
+
+ if (drawable)
+ ++(drawable->refCount);
+ return drawable;
+}
+
+int pvrQwsReleaseWindow(PvrQwsDrawable *drawable)
+{
+ if (drawable->type == PvrQwsWindow)
+ return (--(drawable->refCount) <= 0);
+ else
+ return 0;
+}
+
+PvrQwsDrawable *pvrQwsCreatePixmap(int width, int height, int screen)
+{
+ PvrQwsDrawable *drawable;
+
+ if (!pvrQwsEnsureScreen(screen))
+ return 0;
+
+ drawable = (PvrQwsDrawable *)calloc(1, sizeof(PvrQwsDrawable));
+ if (!drawable)
+ return 0;
+
+ drawable->type = PvrQwsPixmap;
+ drawable->screen = screen;
+ drawable->pixelFormat = pvrQwsDisplay.screens[screen].pixelFormat;
+ drawable->rect.x = 0;
+ drawable->rect.y = 0;
+ drawable->rect.width = width;
+ drawable->rect.height = height;
+
+ if (!pvrQwsAddDrawable()) {
+ free(drawable);
+ return 0;
+ }
+
+ return drawable;
+}
+
+static void pvrQwsDestroyDrawableForced(PvrQwsDrawable *drawable)
+{
+ /* Remove the drawable from the display's winId list */
+ PvrQwsDrawable *current = pvrQwsDisplay.firstWinId;
+ PvrQwsDrawable *prev = 0;
+ while (current != 0 && current != drawable) {
+ prev = current;
+ current = current->nextWinId;
+ }
+ if (current != 0) {
+ if (prev)
+ prev->nextWinId = current->nextWinId;
+ else
+ pvrQwsDisplay.firstWinId = current->nextWinId;
+ }
+
+ pvrQwsFreeBuffers(drawable);
+ free(drawable);
+
+ --pvrQwsDisplay.numDrawables;
+ if (pvrQwsDisplay.numDrawables == 0)
+ pvrQwsDestroyContext();
+}
+
+void pvrQwsDestroyDrawable(PvrQwsDrawable *drawable)
+{
+ if (drawable && drawable->type != PvrQwsScreen)
+ pvrQwsDestroyDrawableForced(drawable);
+}
+
+PvrQwsDrawableType pvrQwsGetDrawableType(PvrQwsDrawable *drawable)
+{
+ return drawable->type;
+}
+
+void pvrQwsSetVisibleRegion
+ (PvrQwsDrawable *drawable, const PvrQwsRect *rects, int numRects)
+{
+ int index, indexOut;
+ PvrQwsRect *rect;
+ PvrQwsRect *screenRect;
+
+ /* Visible regions don't make sense for pixmaps */
+ if (drawable->type == PvrQwsPixmap)
+ return;
+
+ /* Restrict the number of rectangles to prevent buffer overflow */
+ if (numRects > PVRQWS_MAX_VISIBLE_RECTS)
+ numRects = PVRQWS_MAX_VISIBLE_RECTS;
+ if (numRects > 0)
+ memcpy(drawable->visibleRects, rects, numRects * sizeof(PvrQwsRect));
+
+ /* Convert the rectangles into screen-relative co-ordinates and
+ then clamp them to the screen boundaries. If any of the
+ clamped rectangles are empty, remove them from the list */
+ screenRect = &(pvrQwsDisplay.screens[drawable->screen].screenRect);
+ indexOut = 0;
+ for (index = 0, rect = drawable->visibleRects; index < numRects; ++index, ++rect) {
+ if (rect->x < 0) {
+ rect->width += rect->x;
+ rect->x = 0;
+ if (rect->width < 0)
+ rect->width = 0;
+ } else if (rect->x >= screenRect->width) {
+ rect->x = screenRect->width;
+ rect->width = 0;
+ }
+ if ((rect->x + rect->width) > screenRect->width) {
+ rect->width = screenRect->width - rect->x;
+ }
+ if (rect->y < 0) {
+ rect->height += rect->y;
+ rect->y = 0;
+ if (rect->height < 0)
+ rect->height = 0;
+ } else if (rect->y >= screenRect->height) {
+ rect->y = screenRect->height;
+ rect->height = 0;
+ }
+ if ((rect->y + rect->height) > screenRect->height) {
+ rect->height = screenRect->height - rect->y;
+ }
+ if (rect->width > 0 && rect->height > 0) {
+ if (index != indexOut)
+ drawable->visibleRects[indexOut] = *rect;
+ ++indexOut;
+ }
+ }
+ drawable->numVisibleRects = indexOut;
+}
+
+void pvrQwsClearVisibleRegion(PvrQwsDrawable *drawable)
+{
+ if (drawable->type != PvrQwsPixmap)
+ drawable->numVisibleRects = 0;
+}
+
+void pvrQwsSetGeometry(PvrQwsDrawable *drawable, const PvrQwsRect *rect)
+{
+ /* We can only change the geometry of window drawables */
+ if (drawable->type != PvrQwsWindow)
+ return;
+
+ /* If the position has changed, then clear the visible region */
+ if (drawable->rect.x != rect->x || drawable->rect.y != rect->y) {
+ drawable->rect.x = rect->x;
+ drawable->rect.y = rect->y;
+ drawable->numVisibleRects = 0;
+ }
+
+ /* If the size has changed, then clear the visible region and
+ invalidate the drawable's buffers. Invalidating the buffers
+ will force EGL to recreate the drawable, which will then
+ allocate new buffers for the new size */
+ if (drawable->rect.width != rect->width ||
+ drawable->rect.height != rect->height) {
+ drawable->rect.width = rect->width;
+ drawable->rect.height = rect->height;
+ drawable->numVisibleRects = 0;
+ pvrQwsInvalidateBuffers(drawable);
+ }
+}
+
+void pvrQwsGetGeometry(PvrQwsDrawable *drawable, PvrQwsRect *rect)
+{
+ *rect = drawable->rect;
+}
+
+void pvrQwsSetRotation(PvrQwsDrawable *drawable, int angle)
+{
+ if (drawable->rotationAngle != angle) {
+ drawable->rotationAngle = angle;
+
+ /* Force the buffers to be recreated if the rotation angle changes */
+ pvrQwsInvalidateBuffers(drawable);
+ }
+}
+
+int pvrQwsGetStride(PvrQwsDrawable *drawable)
+{
+ if (drawable->backBuffersValid)
+ return drawable->strideBytes;
+ else
+ return 0;
+}
+
+PvrQwsPixelFormat pvrQwsGetPixelFormat(PvrQwsDrawable *drawable)
+{
+ return (PvrQwsPixelFormat)(drawable->pixelFormat);
+}
+
+void *pvrQwsGetRenderBuffer(PvrQwsDrawable *drawable)
+{
+ if (drawable->backBuffersValid)
+ return drawable->backBuffers[drawable->currentBackBuffer]->pBase;
+ else
+ return 0;
+}
+
+int pvrQwsAllocBuffers(PvrQwsDrawable *drawable)
+{
+ int index;
+ int numBuffers = PVRQWS_MAX_BACK_BUFFERS;
+ if (drawable->type == PvrQwsPixmap)
+ numBuffers = 1;
+ if (drawable->backBuffers[0]) {
+ if (drawable->backBuffersValid)
+ return 1;
+ if (!drawable->usingFlipBuffers) {
+ for (index = 0; index < numBuffers; ++index)
+ PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]);
+ }
+ }
+ drawable->stridePixels = (drawable->rect.width + 31) & ~31;
+ drawable->strideBytes =
+ drawable->stridePixels *
+ pvrQwsDisplay.screens[drawable->screen].bytesPerPixel;
+ drawable->usingFlipBuffers =
+ (pvrQwsDisplay.numFlipBuffers > 0 && drawable->isFullScreen);
+ if (drawable->usingFlipBuffers) {
+ if (numBuffers > (int)(pvrQwsDisplay.numFlipBuffers))
+ numBuffers = pvrQwsDisplay.numFlipBuffers;
+ for (index = 0; index < numBuffers; ++index)
+ drawable->backBuffers[index] = pvrQwsDisplay.flipBuffers[index];
+ } else {
+ for (index = 0; index < numBuffers; ++index) {
+ if (PVR2DMemAlloc(pvrQwsDisplay.context,
+ drawable->strideBytes * drawable->rect.height,
+ 128, 0,
+ &(drawable->backBuffers[index])) != PVR2D_OK) {
+ while (--index >= 0)
+ PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]);
+ memset(drawable->backBuffers, 0, sizeof(drawable->backBuffers));
+ drawable->backBuffersValid = 0;
+ return 0;
+ }
+ }
+ }
+ for (index = numBuffers; index < PVRQWS_MAX_BACK_BUFFERS; ++index) {
+ drawable->backBuffers[index] = drawable->backBuffers[0];
+ }
+ drawable->backBuffersValid = 1;
+ drawable->currentBackBuffer = 0;
+ return 1;
+}
+
+void pvrQwsFreeBuffers(PvrQwsDrawable *drawable)
+{
+ int index;
+ int numBuffers = PVRQWS_MAX_BACK_BUFFERS;
+ if (drawable->type == PvrQwsPixmap)
+ numBuffers = 1;
+ if (!drawable->usingFlipBuffers) {
+ for (index = 0; index < numBuffers; ++index) {
+ if (drawable->backBuffers[index])
+ PVR2DMemFree(pvrQwsDisplay.context, drawable->backBuffers[index]);
+ }
+ }
+ memset(drawable->backBuffers, 0, sizeof(drawable->backBuffers));
+ drawable->backBuffersValid = 0;
+ drawable->usingFlipBuffers = 0;
+}
+
+void pvrQwsInvalidateBuffers(PvrQwsDrawable *drawable)
+{
+ drawable->backBuffersValid = 0;
+}
+
+int pvrQwsGetBuffers
+ (PvrQwsDrawable *drawable, PVR2DMEMINFO **source, PVR2DMEMINFO **render)
+{
+ if (!drawable->backBuffersValid)
+ return 0;
+ *render = drawable->backBuffers[drawable->currentBackBuffer];
+ *source = drawable->backBuffers
+ [(drawable->currentBackBuffer + PVRQWS_MAX_BACK_BUFFERS - 1) %
+ PVRQWS_MAX_BACK_BUFFERS];
+ return 1;
+}
+
+int pvrQwsSwapBuffers(PvrQwsDrawable *drawable, int repaintOnly)
+{
+ PVR2DMEMINFO *buffer;
+ PvrQwsRect *rect;
+ int index;
+
+ /* Bail out if the back buffers have been invalidated */
+ if (!drawable->backBuffersValid)
+ return 0;
+
+ /* If there is a swap function, then use that instead */
+ if (drawable->swapFunction) {
+ (*(drawable->swapFunction))(drawable, drawable->userData, repaintOnly);
+ if (!repaintOnly) {
+ drawable->currentBackBuffer
+ = (drawable->currentBackBuffer + 1) % PVRQWS_MAX_BACK_BUFFERS;
+ }
+ return 1;
+ }
+
+ /* Iterate through the visible rectangles and blit them to the screen */
+ if (!repaintOnly) {
+ index = drawable->currentBackBuffer;
+ } else {
+ index = (drawable->currentBackBuffer + PVRQWS_MAX_BACK_BUFFERS - 1)
+ % PVRQWS_MAX_BACK_BUFFERS;
+ }
+ buffer = drawable->backBuffers[index];
+ rect = drawable->visibleRects;
+ if (drawable->usingFlipBuffers) {
+ PVR2DPresentFlip(pvrQwsDisplay.context, pvrQwsDisplay.flipChain, buffer, 0);
+ } else if (pvrQwsDisplay.usePresentBlit && drawable->numVisibleRects > 0) {
+ PVR2DRECT pvrRects[PVRQWS_MAX_VISIBLE_RECTS];
+ for (index = 0; index < drawable->numVisibleRects; ++index, ++rect) {
+ pvrRects[index].left = rect->x;
+ pvrRects[index].top = rect->y;
+ pvrRects[index].right = rect->x + rect->width;
+ pvrRects[index].bottom = rect->y + rect->height;
+ }
+ for (index = 0; index < drawable->numVisibleRects; index += 4) {
+ int numClip = drawable->numVisibleRects - index;
+ if (numClip > 4) /* No more than 4 clip rects at a time */
+ numClip = 4;
+ PVR2DSetPresentBltProperties
+ (pvrQwsDisplay.context,
+ PVR2D_PRESENT_PROPERTY_SRCSTRIDE |
+ PVR2D_PRESENT_PROPERTY_DSTSIZE |
+ PVR2D_PRESENT_PROPERTY_DSTPOS |
+ PVR2D_PRESENT_PROPERTY_CLIPRECTS,
+ drawable->strideBytes,
+ drawable->rect.width, drawable->rect.height,
+ drawable->rect.x, drawable->rect.y,
+ numClip, pvrRects + index, 0);
+ PVR2DPresentBlt(pvrQwsDisplay.context, buffer, 0);
+ }
+ PVR2DQueryBlitsComplete(pvrQwsDisplay.context, buffer, 1);
+ } else {
+ /* TODO: use PVR2DBltClipped for faster transfers of clipped windows */
+ PVR2DBLTINFO blit;
+ for (index = 0; index < drawable->numVisibleRects; ++index, ++rect) {
+ memset(&blit, 0, sizeof(blit));
+
+ blit.CopyCode = PVR2DROPcopy;
+ blit.BlitFlags = PVR2D_BLIT_DISABLE_ALL;
+
+ blit.pSrcMemInfo = buffer;
+ blit.SrcStride = drawable->strideBytes;
+ blit.SrcX = rect->x - drawable->rect.x;
+ blit.SrcY = rect->y - drawable->rect.y;
+ blit.SizeX = rect->width;
+ blit.SizeY = rect->height;
+ blit.SrcFormat = drawable->pixelFormat;
+
+ blit.pDstMemInfo = pvrQwsDisplay.screens[drawable->screen].frameBuffer;
+ blit.DstStride = pvrQwsDisplay.screens[drawable->screen].screenStride;
+ blit.DstX = rect->x;
+ blit.DstY = rect->y;
+ blit.DSizeX = rect->width;
+ blit.DSizeY = rect->height;
+ blit.DstFormat = pvrQwsDisplay.screens[drawable->screen].pixelFormat;
+
+ PVR2DBlt(pvrQwsDisplay.context, &blit);
+ }
+ }
+
+ /* Swap the buffers */
+ if (!repaintOnly) {
+ drawable->currentBackBuffer
+ = (drawable->currentBackBuffer + 1) % PVRQWS_MAX_BACK_BUFFERS;
+ }
+ return 1;
+}
+
+void pvrQwsSetSwapFunction
+ (PvrQwsDrawable *drawable, PvrQwsSwapFunction func, void *userData)
+{
+ drawable->swapFunction = func;
+ drawable->userData = userData;
+}