/* 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. * * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ * */ #include #include #include #include #include "common/scummsys.h" #include "backends/platform/psp/psppixelformat.h" #include "backends/platform/psp/display_client.h" #include "backends/platform/psp/display_manager.h" #include "backends/platform/psp/memory.h" //#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */ //#define __PSP_DEBUG_PRINT__ #include "backends/platform/psp/trace.h" #define PSP_BUFFER_WIDTH (512) #define PSP_SCREEN_WIDTH 480 #define PSP_SCREEN_HEIGHT 272 #define PSP_FRAME_SIZE (PSP_BUFFER_WIDTH * PSP_SCREEN_HEIGHT) DisplayManager *GuRenderer::_displayManager = 0; // class Palette ------------------------------------------------------------ // void Palette::clear() { DEBUG_ENTER_FUNC(); if (_values && _numOfEntries) memset(_values, 0, getSizeInBytes()); PSP_DEBUG_PRINT("_values[%p]\n", _values); } // Used to clear the specific keycolor // void Palette::setColorPositionAlpha(uint32 position, bool alpha) { DEBUG_ENTER_FUNC(); assert(_values); assert(position < _numOfEntries); PSP_DEBUG_PRINT("position[%d], numofEntries[%u], bpp[%u], values[%p]\n", position, _numOfEntries, _pixelFormat.bitsPerPixel, _values); if (_numOfEntries <= 16) position &= 0xF; else if (_numOfEntries <= 256) position &= 0xFF; switch (_pixelFormat.bitsPerPixel) { case 16: { uint16 *shortVal = (uint16 *) & _values[_pixelFormat.pixelsToBytes(position)]; *shortVal = _pixelFormat.setColorAlpha((uint32) * shortVal, alpha ? 255 : 0); } break; case 32: { uint32 *wordVal = (uint32 *) & _values[_pixelFormat.pixelsToBytes(position)]; *wordVal = _pixelFormat.setColorAlpha((uint32) * wordVal, alpha ? 255 : 0); } break; default: PSP_ERROR("Incorrect bits per pixel value[%u]\n", _pixelFormat.bitsPerPixel); } } // Set some of the palette to color values in array // By default, ScummVm doesn't support alpha values in palettes void Palette::setPartial(const byte *colors, uint32 start, uint32 num, bool supportsAlpha /* = false */) { DEBUG_ENTER_FUNC(); assert(_values); assert(_numOfEntries); const byte *src = colors; if (start + num > _numOfEntries) // Check boundary num = _numOfEntries - start; if (_pixelFormat.bitsPerPixel == 16) { uint16 *palette = (uint16 *)_values; palette += start; for (uint32 i = 0; i < num; ++i) { byte alphaVal = supportsAlpha ? src[3] : 0xFF; *palette = (uint16)_pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal); src += 4; palette++; } } else if (_pixelFormat.bitsPerPixel == 32) { uint32 *palette = (uint32 *)_values; palette += start; for (uint32 i = 0; i < num; ++i) { byte alphaVal = supportsAlpha ? src[3] : 0xFF; *palette = _pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal); src += 4; palette++; } } } // Sets pixel format and number of entries by the buffer's pixel format */ void Palette::setPixelFormats(PSPPixelFormat::Type paletteType, PSPPixelFormat::Type bufferType, bool swapRedBlue /* = false */) { DEBUG_ENTER_FUNC(); if (paletteType == PSPPixelFormat::Type_Unknown) PSP_ERROR("Unknown paletteType[%u]\n", paletteType); switch (bufferType) { case PSPPixelFormat::Type_Palette_8bit: _numOfEntries = 256; break; case PSPPixelFormat::Type_Palette_4bit: _numOfEntries = 16; break; case PSPPixelFormat::Type_Unknown: case PSPPixelFormat::Type_None: PSP_ERROR("Unhandled bufferType[%u]\n", bufferType); break; default: // No palette _numOfEntries = 0; break; } _pixelFormat.set(paletteType, swapRedBlue); } bool Palette::allocate() { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("_numOfEntries[%u]\n", _numOfEntries); PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel); if (_values) { free(CACHED(_values)); _values = 0; } // We allocate on 64bytes to get a cache line, and round up to 64bytes to get the full line uint32 amountInBytes = getSizeInBytes(); if (amountInBytes < 64) amountInBytes = 64; _values = (byte *)memalign(64, amountInBytes); // Use uncached memory GuRenderer::cacheInvalidate(_values, amountInBytes); _values = UNCACHED(_values); if (!_values) { PSP_ERROR("Couldn't allocate palette.\n"); return false; } PSP_DEBUG_PRINT("_values[%p]\n", _values); clear(); return true; } void Palette::deallocate() { DEBUG_ENTER_FUNC(); free(CACHED(_values)); _values = 0; _numOfEntries = 0; } // Copy some of the palette to an array of colors // void Palette::getPartial(byte *colors, uint start, uint num) { DEBUG_ENTER_FUNC(); assert(_values); assert(_numOfEntries); uint32 r, g, b, a; if (start + num > _numOfEntries) // Check boundary num = _numOfEntries - start; if (_pixelFormat.bitsPerPixel == 16) { uint16 *palette = (uint16 *)_values; palette += start; for (uint32 i = start; i < start + num; i++) { _pixelFormat.colorToRgba(*palette, r, g, b, a); *colors++ = (byte)r; *colors++ = (byte)g; *colors++ = (byte)b; *colors++ = (byte)a; palette++; } } else if (_pixelFormat.bitsPerPixel == 32) { uint32 *palette = (uint32 *)_values; palette += start; for (uint32 i = start; i < start + num; i++) { _pixelFormat.colorToRgba(*palette, r, g, b, a); *colors++ = (byte)r; *colors++ = (byte)g; *colors++ = (byte)b; *colors++ = (byte)a; palette++; } } } void Palette::setSingleColorRGBA(uint32 num, byte r, byte g, byte b, byte a) { // DEBUG_ENTER_FUNC(); uint16 *shortValues; uint32 *wordValues; assert(_values); assert(num < _numOfEntries); switch (_pixelFormat.bitsPerPixel) { case 16: shortValues = (uint16 *)_values; shortValues[num] = _pixelFormat.rgbaToColor(r, g, b, a); break; case 32: wordValues = (uint32 *)_values; wordValues[num] = _pixelFormat.rgbaToColor(r, g, b, a); break; default: PSP_ERROR("Incorrect bitsPerPixel[%d]\n", _pixelFormat.bitsPerPixel); break; } } // Print to screen void Palette::print(uint32 numToPrint /* = 0 */) { if (_numOfEntries > 0) { assert(_values); if (numToPrint > _numOfEntries || numToPrint == 0) numToPrint = _numOfEntries; PSP_INFO_PRINT("cursor palette:\n"); for (unsigned int i = 0; i < numToPrint; i++) { byte *pcolor = &_values[_pixelFormat.pixelsToBytes(i)]; uint32 color = _pixelFormat.getColorValueAt(pcolor); PSP_INFO_PRINT("[%u=%x] ", i, color); } PSP_INFO_PRINT("\n"); } } uint32 Palette::getRawColorAt(uint32 position) { byte *pcolor = &_values[_pixelFormat.pixelsToBytes(position)]; uint32 color = _pixelFormat.getColorValueAt(pcolor); return color; } uint32 Palette::getRGBAColorAt(uint32 position) { uint32 color = getRawColorAt(position); uint32 r, g, b, a; _pixelFormat.colorToRgba(color, r, g, b, a); return (a << 24 | b << 16 | g << 8 | r); } // class Buffer --------------------------------------------------- void Buffer::setPixelFormat(PSPPixelFormat::Type type, bool swapRedBlue) { if (type == PSPPixelFormat::Type_None || type == PSPPixelFormat::Type_Unknown) PSP_ERROR("Unhandled buffer format[%u]\n", type); _pixelFormat.set(type, swapRedBlue); } bool Buffer::hasPalette() { if (_pixelFormat.format == PSPPixelFormat::Type_Palette_8bit || _pixelFormat.format == PSPPixelFormat::Type_Palette_4bit) return true; return false; } /* pitch is in bytes */ void Buffer::copyFromArray(const byte *buffer, int pitch) { DEBUG_ENTER_FUNC(); // We use sourceSize because outside, they won't know what the true size is copyFromRect(buffer, pitch, 0, 0, _sourceSize.width, _sourceSize.height); } /* pitch is in bytes */ void Buffer::copyFromRect(const byte *buf, uint32 pitch, int destX, int destY, uint32 recWidth, uint32 recHeight) { // Removed silly clipping code DEBUG_ENTER_FUNC(); assert(_pixels); if (recWidth > _sourceSize.width - destX) { recWidth = _sourceSize.width - destX; } if (recHeight > _sourceSize.height - destY) { recHeight = _sourceSize.height - destY; } if (recWidth <= 0 || recHeight <= 0) { return; } byte *dst = _pixels + _pixelFormat.pixelsToBytes((destY * _width) + destX); uint32 recWidthInBytes = _pixelFormat.pixelsToBytes(recWidth); uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width); if (pitch == realWidthInBytes && pitch == recWidthInBytes) { //memcpy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth)); Copier::copy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth), &_pixelFormat); } else { do { //memcpy(dst, buf, recWidthInBytes); Copier::copy(dst, buf, recWidthInBytes, &_pixelFormat); buf += pitch; dst += realWidthInBytes; } while (--recHeight); } } /* pitch is in bytes */ void Buffer::copyToArray(byte *dst, int pitch) { DEBUG_ENTER_FUNC(); assert(_pixels); uint32 h = _height; byte *src = _pixels; uint32 sourceWidthInBytes = _pixelFormat.pixelsToBytes(_sourceSize.width); uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width); do { //memcpy(dst, src, sourceWidthInBytes); Copier::copy(dst, src, sourceWidthInBytes, &_pixelFormat); src += realWidthInBytes; dst += pitch; } while (--h); } /* We can size the buffer either by texture size (multiple of 2^n) or source size. The GU can really handle both, but is supposed to get only 2^n size buffers */ void Buffer::setSize(uint32 width, uint32 height, HowToSize textureOrSource/*=kSizeByTextureSize*/) { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("w[%u], h[%u], %s\n", width, height, textureOrSource ? "size by source" : "size by texture"); _sourceSize.width = width; _sourceSize.height = height; _textureSize.width = scaleUpToPowerOfTwo(width); _textureSize.height = scaleUpToPowerOfTwo(height); if (textureOrSource == kSizeByTextureSize) { _width = _textureSize.width; _height = _textureSize.height; } else { /* kSizeBySourceSize */ _width = _sourceSize.width; _height = _sourceSize.height; } } /* Scale a dimension (width/height) up to power of 2 for the texture */ uint32 Buffer::scaleUpToPowerOfTwo(uint32 size) { uint32 textureDimension = 0; if (size <= 16) textureDimension = 16; else if (size <= 32) textureDimension = 32; else if (size <= 64) textureDimension = 64; else if (size <= 128) textureDimension = 128; else if (size <= 256) textureDimension = 256; else textureDimension = 512; PSP_DEBUG_PRINT("power of 2 = %u\n", textureDimension); return textureDimension; } bool Buffer::allocate(bool inVram/*=false*/) { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("_width[%u], _height[%u]\n", _width, _height); PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel); if (_pixels) { if (VramAllocator::isAddressInVram(_pixels)) // Check if in VRAM VramAllocator::instance().deallocate(_pixels); else // not in VRAM free(CACHED(_pixels)); _pixels = 0; } uint32 size = getSizeInBytes(); if (inVram) { _pixels = (byte *)VramAllocator::instance().allocate(size); } if (!_pixels) { // Either we are not in vram or we didn't manage to allocate in vram // Align to 64 bytes. All normal buffer sizes are multiples of 64 anyway _pixels = (byte *)memalign(64, size); } if (!_pixels) { PSP_ERROR("couldn't allocate buffer.\n"); return false; } // Use uncached memory GuRenderer::cacheInvalidate(_pixels, size); _pixels = UNCACHED(_pixels); clear(); return true; } void Buffer::deallocate() { DEBUG_ENTER_FUNC(); if (!_pixels) return; if (VramAllocator::isAddressInVram(_pixels)) // Check if in VRAM VramAllocator::instance().deallocate(_pixels); else free(CACHED(_pixels)); _pixels = 0; } void Buffer::clear() { DEBUG_ENTER_FUNC(); if (_pixels) memset(_pixels, 0, getSizeInBytes()); } /* Convert 4 bit images to match weird PSP format */ void Buffer::flipNibbles() { DEBUG_ENTER_FUNC(); if (_pixelFormat.bitsPerPixel != 4) return; assert(_pixels); uint32 *dest = (uint32 *)_pixels; for (uint32 i = 0; i < getSourceHeight(); i++) { for (uint32 j = 0; j < (getWidth() >> 3); j++) { // /8 because we do it in 32bit chunks uint32 val = *dest; *dest++ = ((val >> 4) & 0x0F0F0F0F) | ((val << 4) & 0xF0F0F0F0); } } } // Print buffer contents to screen (only source size is printed out) void Buffer::print(uint32 mask, uint32 numToPrint /*=0*/) { assert(_pixels); if (numToPrint > _sourceSize.width * _sourceSize.height || numToPrint == 0) numToPrint = _sourceSize.width * _sourceSize.height; PSP_INFO_PRINT("buffer: \n"); PSP_INFO_PRINT("width[%u], height[%u]\n\n", _sourceSize.width, _sourceSize.height); for (unsigned int i = 0; i < _sourceSize.height; i++) { for (unsigned int j = 0; j < _sourceSize.width; j++) { if (numToPrint <= 0) // check if done break; byte *pcolor = &_pixels[_pixelFormat.pixelsToBytes((i * _width) + j)]; uint32 color = _pixelFormat.getColorValueAt(pcolor); //if (color != 0) PSP_INFO_PRINT("[%x] ", color); PSP_INFO_PRINT("[%x] ", mask & color); numToPrint--; } PSP_INFO_PRINT("\n"); } PSP_INFO_PRINT("\n"); } // class GuRenderer ------------------------------------------------- //#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */ //#define __PSP_DEBUG_PRINT__ #include "backends/platform/psp/trace.h" void GuRenderer::render() { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("Buffer[%p] Palette[%p]\n", _buffer->getPixels(), _palette->getRawValues()); setMaxTextureOffsetByIndex(0, 0); guProgramDrawBehavior(); if (_buffer->hasPalette()) guLoadPalette(); guProgramTextureFormat(); guLoadTexture(); Vertex *vertices = guGetVertices(); fillVertices(vertices); guDrawVertices(vertices); if (_buffer->getSourceWidth() > 512) { setMaxTextureOffsetByIndex(1, 0); guLoadTexture(); vertices = guGetVertices(); fillVertices(vertices); guDrawVertices(vertices); } } inline void GuRenderer::setMaxTextureOffsetByIndex(uint32 x, uint32 y) { DEBUG_ENTER_FUNC(); const uint32 maxTextureSizeShift = 9; /* corresponds to 512 = max texture size*/ _maxTextureOffset.x = x << maxTextureSizeShift; /* x times 512 */ _maxTextureOffset.y = y << maxTextureSizeShift; /* y times 512 */ } inline void GuRenderer::guProgramDrawBehavior() { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("blending[%s] colorTest[%s] reverseAlpha[%s] keyColor[%u]\n", _blending ? "on" : "off", _colorTest ? "on" : "off", _alphaReverse ? "on" : "off", _keyColor); if (_blending) { sceGuEnable(GU_BLEND); if (_alphaReverse) // Reverse the alpha value (0 is 1) sceGuBlendFunc(GU_ADD, GU_ONE_MINUS_SRC_ALPHA, GU_SRC_ALPHA, 0, 0); else // Normal alpha values sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); } else sceGuDisable(GU_BLEND); if (_colorTest) { sceGuEnable(GU_COLOR_TEST); sceGuColorFunc(GU_NOTEQUAL, _keyColor, 0x00ffffff); } else sceGuDisable(GU_COLOR_TEST); } inline void GuRenderer::guLoadPalette() { DEBUG_ENTER_FUNC(); uint32 mask; if (_buffer->getBitsPerPixel() == 4) mask = 0x0F; else if (_buffer->getBitsPerPixel() == 8) mask = 0xFF; else assert(0); /* error */ PSP_DEBUG_PRINT("numOfEntries[%d]\n", _palette->getNumOfEntries()); PSP_DEBUG_PRINT("bpp[%d], pixelformat[%d], mask[%x]\n", _buffer->getBitsPerPixel(), _palette->getPixelFormat(), mask); sceGuClutMode(convertToGuPixelFormat(_palette->getPixelFormat()), 0, mask, 0); sceGuClutLoad(_palette->getNumOfEntries() >> 3, _palette->getRawValues()); } inline void GuRenderer::guProgramTextureFormat() { DEBUG_ENTER_FUNC(); PSP_DEBUG_PRINT("pixelFormat[%d]\n", _buffer->getPixelFormat()); sceGuTexMode(convertToGuPixelFormat(_buffer->getPixelFormat()), 0, 0, 0); } inline uint32 GuRenderer::convertToGuPixelFormat(PSPPixelFormat::Type format) { DEBUG_ENTER_FUNC(); uint32 guFormat = 0; switch (format) { case PSPPixelFormat::Type_4444: guFormat = GU_PSM_4444; break; case PSPPixelFormat::Type_5551: guFormat = GU_PSM_5551; break; case PSPPixelFormat::Type_5650: guFormat = GU_PSM_5650; break; case PSPPixelFormat::Type_8888: guFormat = GU_PSM_8888; break; case PSPPixelFormat::Type_Palette_8bit: guFormat = GU_PSM_T8; break; case PSPPixelFormat::Type_Palette_4bit: guFormat = GU_PSM_T4; break; default: break; } PSP_DEBUG_PRINT("Pixelformat[%d], guFormat[%d]\n", format, guFormat); return guFormat; } inline void GuRenderer::guLoadTexture() { DEBUG_ENTER_FUNC(); sceGuTexImage(0, _buffer->getTextureWidth(), _buffer->getTextureHeight(), _buffer->getWidth(), _buffer->getPixels() + _buffer->_pixelFormat.pixelsToBytes(_maxTextureOffset.x)); } inline Vertex *GuRenderer::guGetVertices() { DEBUG_ENTER_FUNC(); Vertex *ret = (Vertex *)sceGuGetMemory(2 * sizeof(Vertex)); return ret; } // Fills the vertices. Most of the logic is here. void GuRenderer::fillVertices(Vertex *vertices) { DEBUG_ENTER_FUNC(); uint32 outputWidth = _displayManager->getOutputWidth(); uint32 outputHeight = _displayManager->getOutputHeight(); float textureStartX, textureStartY, textureEndX, textureEndY; // Texture adjustments for eliminating half-pixel artifacts from scaling // Not necessary if we don't scale float textureAdjustment = 0.0f; if (_useGlobalScaler && (_displayManager->getScaleX() != 1.0f || _displayManager->getScaleX() != 1.0f)) textureAdjustment = 0.5f; textureStartX = textureAdjustment + _offsetInBuffer.x; //debug textureStartY = textureAdjustment + _offsetInBuffer.y; // We subtract maxTextureOffset because our shifted texture starts at 512 and will go to 640 textureEndX = _offsetInBuffer.x + _drawSize.width - textureAdjustment - _maxTextureOffset.x; textureEndY = _offsetInBuffer.y + _drawSize.height - textureAdjustment; // For scaling to the final image size, calculate the gaps on both sides uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0; uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0; // Save scaled offset on screen float scaledOffsetOnScreenX = scaleSourceToOutputX(_offsetOnScreen.x); float scaledOffsetOnScreenY = scaleSourceToOutputY(_offsetOnScreen.y); float imageStartX, imageStartY, imageEndX, imageEndY; imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutputX(_maxTextureOffset.x)); imageStartY = gapY + scaledOffsetOnScreenY; if (_fullScreen) { // shortcut imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX; imageEndY = PSP_SCREEN_HEIGHT - gapY + scaledOffsetOnScreenY; // needed for screen shake } else { /* !fullScreen */ imageEndX = imageStartX + scaleSourceToOutputX(_drawSize.width); imageEndY = imageStartY + scaleSourceToOutputY(_drawSize.height); } vertices[0].u = textureStartX; vertices[0].v = textureStartY; vertices[1].u = textureEndX; vertices[1].v = textureEndY; vertices[0].x = imageStartX; vertices[0].y = imageStartY; vertices[0].z = 0; vertices[1].x = imageEndX; vertices[1].y = imageEndY; vertices[1].z = 0; PSP_DEBUG_PRINT("TextureStart: X[%f] Y[%f] TextureEnd: X[%.1f] Y[%.1f]\n", textureStartX, textureStartY, textureEndX, textureEndY); PSP_DEBUG_PRINT("ImageStart: X[%f] Y[%f] ImageEnd: X[%.1f] Y[%.1f]\n", imageStartX, imageStartY, imageEndX, imageEndY); } /* Scale the input X offset to appear in proper position on the screen */ inline float GuRenderer::scaleSourceToOutputX(float offset) { float result; if (!_useGlobalScaler) result = offset; else if (!offset) result = 0.0f; else result = offset * _displayManager->getScaleX(); return result; } /* Scale the input Y offset to appear in proper position on the screen */ inline float GuRenderer::scaleSourceToOutputY(float offset) { float result; if (!_useGlobalScaler) result = offset; else if (!offset) result = 0.0f; else result = offset * _displayManager->getScaleY(); return result; } inline void GuRenderer::guDrawVertices(Vertex *vertices) { DEBUG_ENTER_FUNC(); sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, vertices); } void GuRenderer::cacheInvalidate(void *pointer, uint32 size) { sceKernelDcacheWritebackInvalidateRange(pointer, size); }