diff options
-rw-r--r-- | backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp | 590 | ||||
-rw-r--r-- | backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h | 53 | ||||
-rw-r--r-- | backends/module.mk | 5 | ||||
-rw-r--r-- | backends/platform/sdl/module.mk | 6 | ||||
-rw-r--r-- | backends/platform/sdl/posix/posix-main.cpp | 2 | ||||
-rw-r--r-- | backends/platform/sdl/raspberrypi/README.RASPBERRYPI | 106 | ||||
-rw-r--r-- | backends/platform/sdl/raspberrypi/raspberrypi-main.cpp | 51 | ||||
-rw-r--r-- | backends/platform/sdl/raspberrypi/raspberrypi.cpp | 42 | ||||
-rw-r--r-- | backends/platform/sdl/raspberrypi/raspberrypi.h | 35 | ||||
-rwxr-xr-x | configure | 63 | ||||
-rw-r--r-- | ports.mk | 8 |
11 files changed, 958 insertions, 3 deletions
diff --git a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp new file mode 100644 index 0000000000..7e208db4b7 --- /dev/null +++ b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.cpp @@ -0,0 +1,590 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// Needed for Raspberry Pi header inclusion +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/scummsys.h" + +#if defined(DISPMANX) + +#include "backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h" +#include "graphics/scaler/aspect.h" +#include "common/mutex.h" +#include "common/textconsole.h" + +#include <bcm_host.h> + +struct dispvarsStruct { + DISPMANX_DISPLAY_HANDLE_T display; + DISPMANX_UPDATE_HANDLE_T update; + DISPMANX_ELEMENT_HANDLE_T element; + VC_IMAGE_TYPE_T pixFormat; + VC_DISPMANX_ALPHA_T alpha; + + VC_RECT_T bmpRect; + VC_RECT_T srcRect; + VC_RECT_T dstRect; + uint32_t vcImagePtr; + int screen; + int pitch; + unsigned int dispmanxWidth; + unsigned int dispmanxHeight; + bool aspectRatioCorrection; + void *pixmem; + + int numpages; + dispmanxPage *pages; + dispmanxPage *currentPage; + int pageflipPending; + + pthread_cond_t vsyncCondition; + pthread_mutex_t vsyncCondMutex; + pthread_mutex_t pendingMutex; + + SDL_Surface *fscreen; +}; + +struct dispmanxPage { + DISPMANX_RESOURCE_HANDLE_T resource; + bool used; + // Each page has it's own mutex for + // isolating the access to it's "used" flag. + pthread_mutex_t pageUsedMutex; + + // This field will allow us to access the + // main dispvars struct, for the vsync cb. + struct dispvarsStruct *dispvars; +}; + +DispmanXSdlGraphicsManager::DispmanXSdlGraphicsManager(SdlEventSource *sdlEventSource) + : SurfaceSdlGraphicsManager(sdlEventSource) { + _dispvars = new(dispvarsStruct); + dispmanXInit(); +} + +DispmanXSdlGraphicsManager::~DispmanXSdlGraphicsManager() { + dispmanXVideoQuit(); + delete(_dispvars); +} + +void DispmanXSdlGraphicsManager::dispmanXInit() { + _dispvars->screen = 0; + _dispvars->vcImagePtr = 0; + _dispvars->numpages = 3; + _dispvars->pages = (struct dispmanxPage *)calloc(_dispvars->numpages, sizeof(struct dispmanxPage)); + _dispvars->pageflipPending = 0; + _dispvars->currentPage = NULL; + _dispvars->pixFormat = VC_IMAGE_RGB565; + + /* Transparency disabled */ + _dispvars->alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; + _dispvars->alpha.opacity = 255; + _dispvars->alpha.mask = 0; + _dispvars->element = 0; + + // Init each page's variables + for (int i = 0; i < _dispvars->numpages; i++) { + _dispvars->pages[i].used = false; + _dispvars->pages[i].dispvars = _dispvars; + _dispvars->pages[i].resource = 0; + pthread_mutex_init(&_dispvars->pages[i].pageUsedMutex, NULL); + } + + // Initialize the other mutex and condition variables + pthread_cond_init(&_dispvars->vsyncCondition, NULL); + pthread_mutex_init(&_dispvars->pendingMutex, NULL); + pthread_mutex_init(&_dispvars->vsyncCondMutex, NULL); + + // Before we call any vc_* function, we need to call this one. + bcm_host_init(); + + _dispvars->display = vc_dispmanx_display_open(_dispvars->screen); + graphics_get_display_size(_dispvars->display, &_dispvars->dispmanxWidth, &_dispvars->dispmanxHeight); + + // We need this so SDL_SetVideoMode() is called once. + _dispvars->fscreen = NULL; +} + +void DispmanXSdlGraphicsManager::dispmanXSetup(int srcWidth, int srcHeight) { + unsigned int dstWidth, dstHeight, dstXpos, dstYpos; + + // If we have an element, we have to free it along with it's resources. + if (_dispvars->element) { + dispmanXFreeResources(); + } + + // We do this for 2 bytes per pixel which is default on the Rpi. + _dispvars->pitch = srcWidth * 2; + if (_dispvars->aspectRatioCorrection) { + float aspect = ((float)srcWidth / (float)srcHeight); + dstWidth = _dispvars->dispmanxHeight * aspect; + } else { + dstWidth = _dispvars->dispmanxWidth; + } + dstHeight = _dispvars->dispmanxHeight; + + // If we obtain a scaled image width that is bigger than the physical screen width, + // then we keep the physical screen width as our maximun width. + if (dstWidth > _dispvars->dispmanxWidth) { + dstWidth = _dispvars->dispmanxWidth; + } + + dstXpos = (_dispvars->dispmanxWidth - dstWidth) / 2; + dstYpos = (_dispvars->dispmanxHeight - dstHeight) / 2; + + // Remember we have to transfer the whole bitmap even if we would have + // interest in a part of it! Blitting is done by the GPU. + vc_dispmanx_rect_set(&_dispvars->dstRect, dstXpos, dstYpos, dstWidth, dstHeight); + vc_dispmanx_rect_set(&_dispvars->bmpRect, 0, 0, srcWidth, srcHeight); + vc_dispmanx_rect_set(&_dispvars->srcRect, 0, 0, srcWidth << 16, srcHeight << 16); + + for (int i = 0; i < _dispvars->numpages; i++) { + _dispvars->pages[i].resource = vc_dispmanx_resource_create(_dispvars->pixFormat, + srcWidth, srcHeight, &(_dispvars->vcImagePtr)); + } + + // Add the element. Has to be removed before getting here again. + _dispvars->update = vc_dispmanx_update_start(0); + + _dispvars->element = vc_dispmanx_element_add( + _dispvars->update,_dispvars->display, 0, + &_dispvars->dstRect, 0, + &_dispvars->srcRect, DISPMANX_PROTECTION_NONE, + &_dispvars->alpha, 0, (DISPMANX_TRANSFORM_T)0); + + vc_dispmanx_update_submit_sync(_dispvars->update); +} + +void dispmanXVSyncCallback(DISPMANX_UPDATE_HANDLE_T u, void *arg) { + struct dispmanxPage *page = (struct dispmanxPage*)arg; + struct dispvarsStruct *dispvars = page->dispvars; + + // Marking the page as free must be done before the signaling + // so when the update function continues (it won't continue until we signal) + // we can chose this page as free. + if (dispvars->currentPage) { + pthread_mutex_lock(&dispvars->currentPage->pageUsedMutex); + + // We mark as free the page that was visible until now. + page->dispvars->currentPage->used = false; + + pthread_mutex_unlock(&dispvars->currentPage->pageUsedMutex); + } + + // The page on which we issued the flip that + // caused this callback becomes the visible one + dispvars->currentPage = page; + + // These two things must be isolated "atomically" to avoid getting + // a false positive in the pending_mutex test in update function. + pthread_mutex_lock(&dispvars->pendingMutex); + + dispvars->pageflipPending--; + pthread_cond_signal(&dispvars->vsyncCondition); + + pthread_mutex_unlock(&dispvars->pendingMutex); +} + +void DispmanXSdlGraphicsManager::dispmanXUpdate() { + // Wait until last issued flip completes to get a free page. Also, + // dispmanx doesn't support issuing more than one pageflip. + pthread_mutex_lock(&_dispvars->pendingMutex); + + if (_dispvars->pageflipPending > 0) { + pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->pendingMutex); + } + + pthread_mutex_unlock(&_dispvars->pendingMutex); + + struct dispmanxPage *page = dispmanXGetFreePage(); + + // Frame blitting + vc_dispmanx_resource_write_data(page->resource, _dispvars->pixFormat, + _dispvars->pitch, _dispvars->pixmem, &_dispvars->bmpRect); + + // Issue a page flip at the next vblank interval (will be done at vsync anyway). + _dispvars->update = vc_dispmanx_update_start(0); + + vc_dispmanx_element_change_source(_dispvars->update, _dispvars->element, + page->resource); + vc_dispmanx_update_submit(_dispvars->update, &dispmanXVSyncCallback, page); + + pthread_mutex_lock(&_dispvars->pendingMutex); + _dispvars->pageflipPending++; + pthread_mutex_unlock(&_dispvars->pendingMutex); +} + +struct dispmanxPage *DispmanXSdlGraphicsManager::dispmanXGetFreePage(void) { + struct dispmanxPage *page = NULL; + + while (!page) { + // Try to find a free page + for (int i = 0; i < _dispvars->numpages; ++i) { + if (!_dispvars->pages[i].used) { + page = (_dispvars->pages) + i; + break; + } + } + + // If no page is free at the moment, + // wait until a free page is freed by vsync CB. + if (!page) { + pthread_mutex_lock(&_dispvars->vsyncCondMutex); + pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->vsyncCondMutex); + pthread_mutex_unlock(&_dispvars->vsyncCondMutex); + } + } + + // We mark the choosen page as used + pthread_mutex_lock(&page->pageUsedMutex); + page->used = true; + pthread_mutex_unlock(&page->pageUsedMutex); + + return page; +} + +void DispmanXSdlGraphicsManager::dispmanXFreeResources(void) { + // What if we run into the vsync cb code after freeing the resources? + pthread_mutex_lock(&_dispvars->pendingMutex); + if (_dispvars->pageflipPending > 0) + { + pthread_cond_wait(&_dispvars->vsyncCondition, &_dispvars->pendingMutex); + } + pthread_mutex_unlock(&_dispvars->pendingMutex); + + for (int i = 0; i < _dispvars->numpages; i++) { + vc_dispmanx_resource_delete(_dispvars->pages[i].resource); + _dispvars->pages[i].resource = 0; + _dispvars->pages[i].used = false; + } + + _dispvars->update = vc_dispmanx_update_start(0); + vc_dispmanx_element_remove(_dispvars->update, _dispvars->element); + vc_dispmanx_update_submit_sync(_dispvars->update); + // We use this on the setup function to know if we have to free resources and element. + _dispvars->element = 0; +} + +void DispmanXSdlGraphicsManager::dispmanXVideoQuit() { + // This also waits for pending flips to complete, that's needed before + // we destroy the mutexes and condition. + dispmanXFreeResources(); + + // Destroy the mutexes and conditions + for (int i = 0; i < _dispvars->numpages; i++) { + pthread_mutex_destroy(&_dispvars->pages[i].pageUsedMutex); + } + pthread_mutex_destroy(&_dispvars->pendingMutex); + pthread_mutex_destroy(&_dispvars->vsyncCondMutex); + pthread_cond_destroy(&_dispvars->vsyncCondition); + + free(_dispvars->pages); + + // Close display and deinit + vc_dispmanx_display_close(_dispvars->display); + bcm_host_deinit(); +} + +bool DispmanXSdlGraphicsManager::loadGFXMode() { + _forceFull = true; + + // In dispmanX, we manage aspect ratio correction, so for scummvm it's always disabled. + _videoMode.aspectRatioCorrection = false; + + _videoMode.overlayWidth = _videoMode.screenWidth; + _videoMode.overlayHeight = _videoMode.screenHeight; + _videoMode.hardwareWidth = _videoMode.screenWidth; + _videoMode.hardwareHeight = _videoMode.screenHeight; + + // + // Create the surface that contains the 8 bit game data + // + + _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.screenWidth, _videoMode.screenHeight, + _screenFormat.bytesPerPixel << 3, + ((1 << _screenFormat.rBits()) - 1) << _screenFormat.rShift , + ((1 << _screenFormat.gBits()) - 1) << _screenFormat.gShift , + ((1 << _screenFormat.bBits()) - 1) << _screenFormat.bShift , + ((1 << _screenFormat.aBits()) - 1) << _screenFormat.aShift ); + if (_screen == NULL) + error("allocating _screen failed"); + + // Avoid having SDL_SRCALPHA set even if we supplied an alpha-channel in the format. + SDL_SetAlpha(_screen, 0, 255); + + // We set our own default palette to all black. + SDL_SetColors(_screen, _currentPalette, 0, 256); + + // + // Create the surface that contains the scaled graphics in 16 bit mode + // + + dispmanXSetup(_videoMode.screenWidth, _videoMode.screenHeight); + + _hwscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.hardwareWidth, _videoMode.hardwareHeight, 16, + 0, 0, 0, 0); + + // This is just so SDL 1.x input is initialized. Only once! + if (_dispvars->fscreen == NULL) + _dispvars->fscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16, SDL_FULLSCREEN); + if (_hwscreen == NULL) { + // Don't use error here because we don't have access to the debug console + warning("Allocating surface for dispmanX rendering _hwscreen failed"); + g_system->quit(); + } + + // We render to dispmanx resources from _hwscreen pixels array + _dispvars->pixmem = _hwscreen->pixels; + + _overlayscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, _videoMode.overlayWidth, _videoMode.overlayHeight, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + + if (_overlayscreen == NULL) + error("allocating _overlayscreen failed"); + + _overlayFormat.bytesPerPixel = _overlayscreen->format->BytesPerPixel; + + _overlayFormat.rLoss = _overlayscreen->format->Rloss; + _overlayFormat.gLoss = _overlayscreen->format->Gloss; + _overlayFormat.bLoss = _overlayscreen->format->Bloss; + _overlayFormat.aLoss = _overlayscreen->format->Aloss; + + _overlayFormat.rShift = _overlayscreen->format->Rshift; + _overlayFormat.gShift = _overlayscreen->format->Gshift; + _overlayFormat.bShift = _overlayscreen->format->Bshift; + _overlayFormat.aShift = _overlayscreen->format->Ashift; + +#ifdef USE_OSD + _osdSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, + _hwscreen->w, + _hwscreen->h, + 16, + _hwscreen->format->Rmask, + _hwscreen->format->Gmask, + _hwscreen->format->Bmask, + _hwscreen->format->Amask); + if (_osdSurface == NULL) + error("allocating _osdSurface failed"); + SDL_SetColorKey(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kOSDColorKey); +#endif + + _eventSource->resetKeyboadEmulation( + _videoMode.screenWidth, effectiveScreenHeight()); + + return true; +} + +void DispmanXSdlGraphicsManager::clearOverlay() { + //assert(_transactionMode == kTransactionNone); + + Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + + if (!_overlayVisible) + return; + + // Clear the overlay by making the game screen "look through" everywhere. + SDL_Rect src, dst; + src.x = src.y = 0; + dst.x = dst.y = 0; + src.w = dst.w = _videoMode.screenWidth; + src.h = dst.h = _videoMode.screenHeight; + if (SDL_BlitSurface(_screen, &src, _hwscreen, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + + SDL_LockSurface(_hwscreen); + SDL_LockSurface(_overlayscreen); + Normal1x((byte *)(_hwscreen->pixels), _hwscreen->pitch, + (byte *)_overlayscreen->pixels, _overlayscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight); + + SDL_UnlockSurface(_hwscreen); + SDL_UnlockSurface(_overlayscreen); + + _forceFull = true; +} + +void DispmanXSdlGraphicsManager::internUpdateScreen() { + SDL_Surface *srcSurf, *origSurf; + int height, width; + + // If the shake position changed, fill the dirty area with blackness + if (_currentShakePos != _newShakePos || + (_mouseNeedsRedraw && _mouseBackup.y <= _currentShakePos)) { + SDL_Rect blackrect = {0, 0, (Uint16)(_videoMode.screenWidth * _videoMode.scaleFactor), (Uint16)(_newShakePos * _videoMode.scaleFactor)}; + + if (_dispvars->aspectRatioCorrection && !_overlayVisible) + blackrect.h = real2Aspect(blackrect.h - 1) + 1; + + SDL_FillRect(_hwscreen, &blackrect, 0); + + _currentShakePos = _newShakePos; + + _forceFull = true; + } + + // Check whether the palette was changed in the meantime and update the + // screen surface accordingly. + if (_screen && _paletteDirtyEnd != 0) { + SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart, + _paletteDirtyStart, + _paletteDirtyEnd - _paletteDirtyStart); + + _paletteDirtyEnd = 0; + + _forceFull = true; + } + +#ifdef USE_OSD + // OSD visible (i.e. non-transparent)? + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + // Updated alpha value + const int diff = SDL_GetTicks() - _osdFadeStartTime; + if (diff > 0) { + if (diff >= kOSDFadeOutDuration) { + // Back to full transparency + _osdAlpha = SDL_ALPHA_TRANSPARENT; + } else { + // Do a linear fade out... + const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100; + _osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration; + } + SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha); + _forceFull = true; + } + } +#endif + + if (!_overlayVisible) { + origSurf = _screen; + srcSurf = _hwscreen; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; + } else { + origSurf = _overlayscreen; + srcSurf = _hwscreen; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; + } + + // Add the area covered by the mouse cursor to the list of dirty rects if + // we have to redraw the mouse. + if (_mouseNeedsRedraw) + undrawMouse(); + + // Force a full redraw if requested + if (_forceFull) { + _numDirtyRects = 1; + _dirtyRectList[0].x = 0; + _dirtyRectList[0].y = 0; + _dirtyRectList[0].w = width; + _dirtyRectList[0].h = height; + } + + // Only draw anything if necessary + if (_numDirtyRects > 0 || _mouseNeedsRedraw) { + SDL_Rect *r; + SDL_Rect dst; + SDL_Rect *lastRect = _dirtyRectList + _numDirtyRects; + + for (r = _dirtyRectList; r != lastRect; ++r) { + dst = *r; + + if (SDL_BlitSurface(origSurf, r, srcSurf, &dst) != 0) + error("SDL_BlitSurface failed: %s", SDL_GetError()); + } + + // Readjust the dirty rect list in case we are doing a full update. + // This is necessary if shaking is active. + if (_forceFull) { + _dirtyRectList[0].y = 0; + _dirtyRectList[0].h = effectiveScreenHeight(); + } + + drawMouse(); + +#ifdef USE_OSD + if (_osdAlpha != SDL_ALPHA_TRANSPARENT) { + SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0); + } +#endif + + // Finally, blit all our changes to the screen + if (!_displayDisabled) { + SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList); + dispmanXUpdate(); + } + } + + _numDirtyRects = 0; + _forceFull = false; + _mouseNeedsRedraw = false; +} + +bool DispmanXSdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) { + + // Ctrl-Alt-a toggles aspect ratio correction + if (key == 'a') { + setFeatureState(OSystem::kFeatureAspectRatioCorrection, !_dispvars->aspectRatioCorrection); +#ifdef USE_OSD + char buffer[128]; + if (_dispvars->aspectRatioCorrection) + sprintf(buffer, "%s", ("Enabled aspect ratio correction")); + else + sprintf(buffer, "%s", ("Disabled aspect ratio correction")); + displayMessageOnOSD(buffer); +#endif + internUpdateScreen(); + return true; + } + + return true; +} + +bool DispmanXSdlGraphicsManager::hasFeature(OSystem::Feature f) { + if (f == OSystem::kFeatureFullscreenMode) { + return false; + } else { + return SurfaceSdlGraphicsManager::hasFeature(f); + } +} + +void DispmanXSdlGraphicsManager::setFullscreenMode(bool enable) { + // Since we're always in fullscreen mode, we do nothing here. +} + +void DispmanXSdlGraphicsManager::setAspectRatioCorrection(bool enable) { + Common::StackLock lock(_graphicsMutex); + // We simply take note on what's the aspect ratio correction activation state. + _dispvars->aspectRatioCorrection = enable; + + // If we have a videomode setup already, call dispmanXSetup() again so aspect ratio + // correction activation/deactivation works from the menu. + if (_oldVideoMode.setup && _dispvars->aspectRatioCorrection == enable) { + dispmanXSetup(_videoMode.screenWidth, _videoMode.screenHeight); + } +} + +#endif diff --git a/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h new file mode 100644 index 0000000000..1866ec14a2 --- /dev/null +++ b/backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKENDS_GRAPHICS_SDL_DISPMANX_H +#define BACKENDS_GRAPHICS_SDL_DISPMANX_H + +#include "backends/graphics/surfacesdl/surfacesdl-graphics.h" + +struct dispvarsStruct; +struct dispmanxPage; + +class DispmanXSdlGraphicsManager : public SurfaceSdlGraphicsManager { +public: + DispmanXSdlGraphicsManager(SdlEventSource *sdlEventSource); + ~DispmanXSdlGraphicsManager(); + bool loadGFXMode(); + void internUpdateScreen(); + bool handleScalerHotkeys(Common::KeyCode key); + void setFullscreenMode(bool enable); + void setAspectRatioCorrection(bool enable); + void clearOverlay(); + bool hasFeature(OSystem::Feature f); +protected: + // Raspberry Pi Dispmanx API + void dispmanXSetup(int width, int height); + void dispmanXInit(); + void dispmanXUpdate(); + dispmanxPage *dispmanXGetFreePage(); + void dispmanXFreeResources(); + void dispmanXVideoQuit(); + dispvarsStruct *_dispvars; +}; + +#endif /* BACKENDS_GRAPHICS_SDL_DISPMANX_H */ diff --git a/backends/module.mk b/backends/module.mk index e5e2905781..7f2fb05489 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -129,6 +129,11 @@ MODULE_OBJS += \ mixer/sdl13/sdl13-mixer.o endif +ifdef DISPMANX +MODULE_OBJS += \ + graphics/dispmanxsdl/dispmanxsdl-graphics.o +endif + ifeq ($(BACKEND),tizen) MODULE_OBJS += \ timer/tizen/timer.o diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk index 74dd506d31..65d12dceed 100644 --- a/backends/platform/sdl/module.mk +++ b/backends/platform/sdl/module.mk @@ -36,6 +36,12 @@ MODULE_OBJS += \ ps3/ps3.o endif +ifdef DISPMANX +MODULE_OBJS += \ + raspberrypi/raspberrypi-main.o \ + raspberrypi/raspberrypi.o +endif + # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) OBJS := $(MODULE_OBJS) $(OBJS) diff --git a/backends/platform/sdl/posix/posix-main.cpp b/backends/platform/sdl/posix/posix-main.cpp index d07db11b0c..492da70eeb 100644 --- a/backends/platform/sdl/posix/posix-main.cpp +++ b/backends/platform/sdl/posix/posix-main.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) +#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(MAEMO) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) && !defined(DISPMANX) #include "backends/platform/sdl/posix/posix.h" #include "backends/plugins/sdl/sdl-provider.h" diff --git a/backends/platform/sdl/raspberrypi/README.RASPBERRYPI b/backends/platform/sdl/raspberrypi/README.RASPBERRYPI new file mode 100644 index 0000000000..f8d872b519 --- /dev/null +++ b/backends/platform/sdl/raspberrypi/README.RASPBERRYPI @@ -0,0 +1,106 @@ +ScummVM-RASPBERRYPI README +============================================================================== + +Notes +============ + +This version of ScummVM is specially tailored to use DispmanX, the native 2D +API on the Raspberry Pi. The idea is that scaling and drawing on a double +buffer with a non-blocking vsync wait is all done using the on-board VideoCore +hardware, thus using only a small fraction of the CPU ScummVM uses when ran +on a clunky, software-scaled and desynced X11 environment using the X11 API. +Thus, running this version under an X11 session is not supported. + +Requirements +============ +- Raspberry Pi 1 or 2 microcomputer. +- Raspbian (Debian) installed on SD card. Other distros may be supported if + they include the VideoCore runtime libraries that Raspbian includes. +-An attached keyboard and mouse, or alternatively joystick. + +Controls +============ + +The standard ScummVM keyboard and mouse controls are used as in any other +GNU/Linux based system. +Use the --joystick parameter if you want to use a joystick instead of the +intended mouse for playing the games (not recommended). + +Installation from binaries +============================== + +We have at least three methods to get the binaries into the Raspbian SD: + +1) Since Debian (Raspbian) includes an ssh service by default, I recommend +keeping the SD card on the Raspberry Pi, and using scp to copy the package over +to your home directory in the Debian filesystem. + +scp scummvm-rpi_<version>.zip pi@<raspberrypi_ip>:/home/pi + +2) If your RaspberryPi has internet access, you can simply use wget to +download the package to your home folder: + +cd ~/ +wget <package_link> + +3) You could also connect the Raspbian SD card to your main PC and, after +mounting it (or being automounted as it would be in most desktop GNU/Linux +systems), copy the package file manually to your home directory. +How to mount an SD and copy files to it is beyond the scope of this README. + +Once we have the package file in our home directory using one of the three +aforementioned methods, we would need to uncompress it: + +unzip scummvm-rpi_<version>.zip + +As a result, a directory containing the scummvm along with this README will be +created. +We can run it by simply changing to our scummvm directory and executing the +scummvm file. + +cd scummvm-rpi +./scummvm + +I recommend copying the games to /home/pi/scummvm-rpi. Adding the games via the menu +works as in any other system ScummVM runs on. + +Building from sources +============================== + +We have two options to build once we have the sources in our main GNU/Linux desktop +class PC or in our Raspberry Pi: + +1) Building on the Raspberry Pi itself, although possible, is an SLOW task for the +little computer unless you use distributed gcc (or distcc for short). + +Local compilation would simply consist of the "standard" GNU/Linux building process: + +cd <sources_dir> + +./configure --enable-dispmanx -disable-debug --enable-release +--enable-optimizations --disable-mt32emu --disable-flac --disable-mad --disable-vorbis +--disable-tremor --disable-fluidsynth --disable-taskbar --disable-timidity --disable-alsa +--disable-scalers --disable-hq-scalers --disable-savegame-timestamp --disable-eventrecorder + +make + +As you can see, we're manually disabling scalers because we prefer dispmanx for that, which +makes scalers unnecessary on a CPU limited platform like this, timestamps because most people +doesn't have an RTC on the Raspberry Pi, and event recorder to save SD card write cycles. +All these are automatically disabled when we crosscompile by passing "--host=raspberrypi", +which is not the case. + +¡¡It will be an SLOW process, taking several hours to complete, unless you +are running distcc against a fast compilation server!! + +2) If we want to build by cross-compiling on a GNU/Linux X86-based computer, +we can find concise instructions for this can be found on the ScummVM wiki: + +http://wiki.scummvm.org/index.php/Compiling_ScummVM/RPI + +NOTE: Distcc is my preferred method as it does cross-compiling totally transparent +(we build ON the Pi but the actual CPU-intensive compilation is made on an external +server), but it involves building a custom gcc version on the compilation server and +configuring a server and client in both the Raspberry Pi and the server. + +Enjoy! diff --git a/backends/platform/sdl/raspberrypi/raspberrypi-main.cpp b/backends/platform/sdl/raspberrypi/raspberrypi-main.cpp new file mode 100644 index 0000000000..cddbcb7ec4 --- /dev/null +++ b/backends/platform/sdl/raspberrypi/raspberrypi-main.cpp @@ -0,0 +1,51 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/platform/sdl/raspberrypi/raspberrypi.h" +#include "backends/plugins/sdl/sdl-provider.h" +#include "common/scummsys.h" +#include "base/main.h" + +#if defined(DISPMANX) +int main(int argc, char* argv[]) { + + // Create our OSystem instance + g_system = new OSystem_SDL_RaspberryPi(); + assert(g_system); + + // Pre initialize the backend + ((OSystem_SDL_RaspberryPi *)g_system)->init(); + +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new SDLPluginProvider()); +#endif + + // Invoke the actual ScummVM main entry point: + int res = scummvm_main(argc, argv); + + // Free OSystem + delete (OSystem_SDL_RaspberryPi *)g_system; + + return res; +} + +#endif diff --git a/backends/platform/sdl/raspberrypi/raspberrypi.cpp b/backends/platform/sdl/raspberrypi/raspberrypi.cpp new file mode 100644 index 0000000000..a3f79fd5b3 --- /dev/null +++ b/backends/platform/sdl/raspberrypi/raspberrypi.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if defined(DISPMANX) + +#include "backends/platform/sdl/raspberrypi/raspberrypi.h" +#include "backends/graphics/dispmanxsdl/dispmanxsdl-graphics.h" + +void OSystem_SDL_RaspberryPi::initBackend() { + // Create the events manager + if (_eventSource == 0) + _eventSource = new SdlEventSource(); + + // Create the graphics manager + if (_graphicsManager == 0) { + _graphicsManager = new DispmanXSdlGraphicsManager(_eventSource); + } + + // Call parent implementation of this method + OSystem_POSIX::initBackend(); +} + +#endif diff --git a/backends/platform/sdl/raspberrypi/raspberrypi.h b/backends/platform/sdl/raspberrypi/raspberrypi.h new file mode 100644 index 0000000000..45e2c505f6 --- /dev/null +++ b/backends/platform/sdl/raspberrypi/raspberrypi.h @@ -0,0 +1,35 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SDL_DISPMANX_COMMON_H +#define SDL_DISPMANX_COMMON_H + +#if defined(DISPMANX) +#include "backends/platform/sdl/posix/posix.h" + +class OSystem_SDL_RaspberryPi : public OSystem_POSIX { +public: + void initBackend(); +}; + +#endif /* DISPMANX */ +#endif /* SDL_DISPMANX_COMMON_H */ @@ -135,6 +135,7 @@ _faad=auto _fluidsynth=auto _opengl=auto _opengles=auto +_dispmanx=no _readline=auto _freetype2=auto _taskbar=auto @@ -867,6 +868,7 @@ Special configuration feature: tizen for Samsung Tizen caanoo for Caanoo dingux for Dingux + raspberrypi for Raspberry Pi dreamcast for Sega Dreamcast ds for Nintendo DS gamecube for Nintendo GameCube @@ -1059,7 +1061,9 @@ for ac_option in $@; do --disable-libunity) _libunity=no ;; --enable-opengl) _opengl=yes ;; --disable-opengl) _opengl=no ;; - --enable-bink) _bink=yes ;; + --enable-dispmanx) _dispmanx=yes ;; + --disable-dispmanx) _dispmanx=no ;; + --enable-bink) _bink=yes ;; --disable-bink) _bink=no ;; --enable-verbose-build) _verbose_build=yes ;; --enable-plugins) _dynamic_modules=yes ;; @@ -1305,6 +1309,13 @@ arm-riscos) _host_os=riscos _host_cpu=arm ;; +raspberrypi) + _host_os=linux + _host_cpu=arm + # This tuple is the one used by the official Rpi toolchain. + # It may change in the future. + _host_alias=bcm2708hardfp + ;; caanoo) _host_os=gph-linux _host_cpu=arm @@ -2559,6 +2570,27 @@ if test -n "$_host"; then _seq_midi=no _port_mk="backends/platform/dingux/dingux.mk" ;; + raspberrypi) + # This is needed because the official cross compiler doesn't have multiarch enabled + # but Raspbian does. + # Be careful as it's the linker (LDFLAGS) which must know about sysroot. + # These are needed to build against Raspbian's libSDL even if we don't activate + # dispmanx support. + LDFLAGS="$LDFLAGS --sysroot=$RPI_ROOT" + LDFLAGS="$LDFLAGS -B$RPI_ROOT/usr/lib/arm-linux-gnueabihf" + LDFLAGS="$LDFLAGS -Xlinker --rpath-link=$RPI_ROOT/usr/lib/arm-linux-gnueabihf" + LDFLAGS="$LDFLAGS -Xlinker --rpath-link=$RPI_ROOT/lib/arm-linux-gnueabihf" + LDFLAGS="$LDFLAGS -Xlinker --rpath-link=$RPI_ROOT/opt/vc/lib" + LDFLAGS="$LDFLAGS -L$RPI_ROOT/opt/vc/lib" + LDFLAGS="$LDFLAGS -L/opt/rpi_root/lib/arm-linux-gnueabihf -L$RPI_ROOT/usr/lib -L$RPI_ROOT/opt/vc/lib -lbcm_host -lvcos" + _savegame_timestamp=no + _eventrec=no + _build_scalers=no + _build_hq_scalers=no + # It makes no sense to have both GL and dispmanx rendering support. + _opengl=no + _opengles=no + ;; dreamcast) append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER" append_var DEFINES "-DDISABLE_TEXT_CONSOLE" @@ -3139,7 +3171,6 @@ case $_backend in ;; esac - # # Determine whether host is POSIX compliant, or at least POSIX # compatible enough to support our POSIX code (including dlsym(), @@ -4073,6 +4104,34 @@ fi define_in_config_if_yes "$_opengl" "USE_OPENGL" define_in_config_if_yes "$_opengles" "USE_GLES" +# Check if Raspberry Pi's Dispmanx graphics have been enabled and everything is in place. +# If dispmanx is disabled, we fall back to plain SDL rendering. +if test "$_dispmanx" = "yes" ; then + echocheck "DispmanX graphics" + _use_dispmanx=no + + # These are only needed if, appart from cross-building for Raspberry Pi, + # we are activating dispmanx support. + DISPMANX_CXXFLAGS="-I$RPI_ROOT/usr/include/arm-linux-gnueabihf -I$RPI_ROOT/opt/vc/include -I$RPI_ROOT/opt/vc/include/interface/vmcs_host/linux -I$RPI_ROOT/opt/vc/include/interface/vcos/pthreads -I$RPI_ROOT/usr/include/SDL" + DISPMANX_LIBS="$RPI_LIBS -L/opt/rpi_root/lib/arm-linux-gnueabihf -L$RPI_ROOT/usr/lib -L$RPI_ROOT/opt/vc/lib -lbcm_host -lvcos -lvchiq_arm" + + cat > $TMPC << EOF +#include <bcm_host.h> + + int main(int argc, char *argv[]) { + bcm_host_init(); +} +EOF + cc_check $DISPMANX_CXXFLAGS $DISPMANX_LIBS && _use_dispmanx=yes + if test "$_use_dispmanx" = "yes"; then + CXXFLAGS="$CXXFLAGS $DISPMANX_CXXFLAGS" + LIBS="$LIBS $DISPMANX_LIBS" + DEFINES="$DEFINES -DDISPMANX" + add_line_to_config_mk 'DISPMANX = 1' + echo $_use_dispmanx + else echo "no" + fi +fi # # Check for nasm @@ -331,5 +331,13 @@ endif @echo Now run @echo "\tgit commit -m 'DISTS: Generated Code::Blocks and MSVC project files'" +# Target to create Raspberry Pi zip containig binary and specific README +raspberrypi_dist: + mkdir -p $(srcdir)/scummvm-rpi + cp $(srcdir)/backends/platform/sdl/raspberrypi/README.RASPBERRYPI $(srcdir)/scummvm-rpi/README + cp $(srcdir)/scummvm $(srcdir)/scummvm-rpi + zip -r scummvm-rpi.zip scummvm-rpi + rm -f -R scummvm-rpi + # Mark special targets as phony .PHONY: deb bundle osxsnap win32dist install uninstall |