From 3f7830743a06eb52bed316436d22aa1fbd776b59 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 12 May 2015 21:01:15 +0200 Subject: [PATCH] Implement basic screen rotation as requested in #297 . Does not work correctly in non-buffered rendering yet. --- Core/Config.cpp | 1 + Core/Config.h | 4 +- GPU/Common/FramebufferCommon.cpp | 74 ++++++++++++++++++---------- GPU/Common/FramebufferCommon.h | 2 + GPU/Directx9/FramebufferDX9.cpp | 62 ++++++++++-------------- GPU/Directx9/FramebufferDX9.h | 7 +-- GPU/GLES/Framebuffer.cpp | 83 +++++++++++--------------------- GPU/GLES/Framebuffer.h | 3 +- UI/GameSettingsScreen.cpp | 6 +++ 9 files changed, 119 insertions(+), 123 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index d5d6ae8dfa..88c1e59d1f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -312,6 +312,7 @@ static ConfigSetting generalSettings[] = { #ifdef ANDROID ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, 1), #endif + ConfigSetting("InternalScreenRotation", &g_Config.iInternalScreenRotation, 1), #if defined(USING_WIN_UI) ConfigSetting("TopMost", &g_Config.bTopMost, false), diff --git a/Core/Config.h b/Core/Config.h index a1da8e3920..6a5600bf3f 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -127,7 +127,9 @@ public: int iLockedCPUSpeed; bool bAutoSaveSymbolMap; bool bCacheFullIsoInRam; - int iScreenRotation; + + int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms. + int iInternalScreenRotation; // The internal screen rotation angle. Useful for vertical SHMUPs and similar. std::string sReportHost; std::vector recentIsos; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 5c1465ca6a..057c71755a 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -26,6 +26,55 @@ #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH) { + float outW; + float outH; + + bool rotated = g_Config.iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || g_Config.iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180; + + if (g_Config.bStretchToDisplay) { + outW = frameW; + outH = frameH; + } else { + // Add special case for 1080p displays, cutting off the bottom and top 1-pixel rows from the original 480x272. + // This will be what 99.9% of users want. + if (origW == 480 && origH == 272 && frameW == 1920 && frameH == 1080 && !rotated) { + *x = 0; + *y = -4; + *w = 1920; + *h = 1088; + return; + } + + float origRatio = !rotated ? origW / origH : origH / origW; + float frameRatio = frameW / frameH; + + if (origRatio > frameRatio) { + // Image is wider than frame. Center vertically. + outW = frameW; + outH = frameW / origRatio; + // Stretch a little bit + if (!rotated && g_Config.bPartialStretch) + outH = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 + } else { + // Image is taller than frame. Center horizontally. + outW = frameH * origRatio; + outH = frameH; + } + } + + if (g_Config.bSmallDisplay) { + outW /= 2.0f; + outH /= 2.0f; + } + + *x = (frameW - outW) / 2.0f; + *y = (frameH - outH) / 2.0f; + *w = outW; + *h = outH; +} + + FramebufferManagerCommon::FramebufferManagerCommon() : displayFramebufPtr_(0), displayStride_(0), @@ -179,31 +228,6 @@ void FramebufferManagerCommon::EstimateDrawingSize(int &drawing_width, int &draw } void FramebufferManagerCommon::DoSetRenderFrameBuffer() { - /* - if (useBufferedRendering_ && currentRenderVfb_) { - // Hack is enabled, and there was a previous framebuffer. - // Before we switch, let's do a series of trickery to copy one bit of stencil to - // destination alpha. Or actually, this is just a bunch of hackery attempts on Wipeout. - // Ignore for now. - glstate.depthTest.disable(); - glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - glstate.stencilTest.enable(); - glstate.stencilOp.set(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil§ - glstate.stencilFunc.set(GL_GEQUAL, 0xFE, 0xFF); - DrawPlainColor(0x00000000); - //glstate.stencilFunc.set(GL_LESS, 0x80, 0xFF); - //DrawPlainColor(0xFF000000); - glstate.stencilTest.disable(); - glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glstate.depthTest.disable(); - glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - DrawPlainColor(0x00000000); - shaderManager_->DirtyLastShader(); // dirty lastShader_ - } - */ - - gstate_c.framebufChanged = false; // Get parameters diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index b4254ba47f..c5e5810055 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -236,3 +236,5 @@ protected: FBO_OLD_USAGE_FLAG = 15, }; }; + +void CenterRect(float *x, float *y, float *w, float *h, float origW, float origH, float frameW, float frameH); diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 335852299d..fce45ec938 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -42,39 +42,6 @@ namespace DX9 { static void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format); - void CenterRect(float *x, float *y, float *w, float *h, - float origW, float origH, float frameW, float frameH) { - if (g_Config.bStretchToDisplay) { - *x = 0; - *y = 0; - *w = frameW; - *h = frameH; - return; - } - - float origRatio = origW/origH; - float frameRatio = frameW/frameH; - - if (origRatio > frameRatio) { - // Image is wider than frame. Center vertically. - float scale = origW / frameW; - *x = 0.0f; - *w = frameW; - *h = frameW / origRatio; - // Stretch a little bit - if (g_Config.bPartialStretch) - *h = (frameH + *h) / 2.0f; // (408 + 720) / 2 = 564 - *y = (frameH - *h) / 2.0f; - } else { - // Image is taller than frame. Center horizontally. - float scale = origH / frameH; - *y = 0.0f; - *h = frameH; - *w = frameH * origRatio; - *x = (frameW - *w) / 2.0f; - } - } - void FramebufferManagerDX9::ClearBuffer() { dxstate.scissorTest.disable(); dxstate.depthWrite.set(TRUE); @@ -234,10 +201,10 @@ namespace DX9 { // (it always runs at output resolution so FXAA may look odd). float x, y, w, h; CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight); - DrawActiveTexture(drawPixelsTex_, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f); + DrawActiveTexture(drawPixelsTex_, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, g_Config.iInternalScreenRotation); } - void FramebufferManagerDX9::DrawActiveTexture(LPDIRECT3DTEXTURE9 tex, float x, float y, float w, float h, float destW, float destH, bool flip, float u0, float v0, float u1, float v1) { + void FramebufferManagerDX9::DrawActiveTexture(LPDIRECT3DTEXTURE9 tex, float x, float y, float w, float h, float destW, float destH, bool flip, float u0, float v0, float u1, float v1, int uvRotation) { if (flip) { std::swap(v0, v1); } @@ -250,6 +217,28 @@ namespace DX9 { x,y+h,0, u0,v1, }; + static const short indices[4] = { 0, 1, 3, 2 }; + + if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { + float temp[8]; + int rotation = 0; + switch (uvRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break; + case ROTATION_LOCKED_VERTICAL: rotation = 1; break; + case ROTATION_LOCKED_VERTICAL180: rotation = 3; break; + } + + for (int i = 0; i < 4; i++) { + temp[i * 2] = coord[((i + rotation) & 3) * 5 + 3]; + temp[i * 2 + 1] = coord[((i + rotation) & 3) * 5 + 4]; + } + + for (int i = 0; i < 4; i++) { + coord[i * 5 + 3] = temp[i * 2]; + coord[i * 5 + 4] = temp[i * 2 + 1]; + } + } + float invDestW = 1.0f / (destW * 0.5f); float invDestH = 1.0f / (destH * 0.5f); float halfPixelX = invDestW * 0.5f; @@ -259,7 +248,6 @@ namespace DX9 { coord[i * 5 + 1] = -(coord[i * 5 + 1] * invDestH - 1.0f - halfPixelY); } - //pD3Ddevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); pD3Ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); pD3Ddevice->SetVertexDeclaration(pFramebufferVertexDecl); pD3Ddevice->SetPixelShader(pFramebufferPixelShader); @@ -779,7 +767,7 @@ namespace DX9 { } dxstate.texMipFilter.set(D3DTEXF_NONE); dxstate.texMipLodBias.set(0); - DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, u0, v0, u1, v1); + DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, u0, v0, u1, v1, g_Config.iInternalScreenRotation); } } /* diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 9dc34f95bd..23e4cc5468 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -32,6 +32,7 @@ #include "Globals.h" #include "GPU/GPUCommon.h" #include "GPU/Common/FramebufferCommon.h" +#include "Core/Config.h" namespace DX9 { @@ -39,10 +40,6 @@ class TextureCacheDX9; class TransformDrawEngineDX9; class ShaderManagerDX9; -void CenterRect(float *x, float *y, float *w, float *h, - float origW, float origH, float frameW, float frameH); - - class FramebufferManagerDX9 : public FramebufferManagerCommon { public: FramebufferManagerDX9(); @@ -62,7 +59,7 @@ public: virtual void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) override; virtual void DrawFramebuffer(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) override; - void DrawActiveTexture(LPDIRECT3DTEXTURE9 texture, float x, float y, float w, float h, float destW, float destH, bool flip = false, float u0 = 0.0f, float v0 = 0.0f, float u1 = 1.0f, float v1 = 1.0f); + void DrawActiveTexture(LPDIRECT3DTEXTURE9 texture, float x, float y, float w, float h, float destW, float destH, bool flip = false, float u0 = 0.0f, float v0 = 0.0f, float u1 = 1.0f, float v1 = 1.0f, int uvRotation = ROTATION_LOCKED_HORIZONTAL); void DestroyAllFBOs(); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index b1f502c42f..47cfa1c052 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -100,53 +100,6 @@ static const char color_vs[] = void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format); -void CenterRect(float *x, float *y, float *w, float *h, - float origW, float origH, float frameW, float frameH) { - float outW; - float outH; - - if (g_Config.bStretchToDisplay) { - outW = frameW; - outH = frameH; - } else { - // Add special case for 1080p displays, cutting off the bottom and top 1-pixel rows from the original 480x272. - // This will be what 99.9% of users want. - if (origW == 480 && origH == 272 && frameW == 1920 && frameH == 1080) { - *x = 0; - *y = -4; - *w = 1920; - *h = 1088; - return; - } - - float origRatio = origW / origH; - float frameRatio = frameW / frameH; - if (origRatio > frameRatio) { - // Image is wider than frame. Center vertically. - outW = frameW; - outH = frameW / origRatio; - // Stretch a little bit - if (g_Config.bPartialStretch) - outH = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 - } - else { - // Image is taller than frame. Center horizontally. - outW = frameH * origRatio; - outH = frameH; - } - } - - if (g_Config.bSmallDisplay) { - outW /= 2.0f; - outH /= 2.0f; - } - - *x = (frameW - outW) / 2.0f; - *y = (frameH - outH) / 2.0f; - *w = outW; - *h = outH; -} - void FramebufferManager::ClearBuffer() { glstate.scissorTest.disable(); glstate.depthWrite.set(GL_TRUE); @@ -467,13 +420,14 @@ void FramebufferManager::DrawFramebuffer(const u8 *srcPixels, GEBufferFormat src // (it always runs at output resolution so FXAA may look odd). float x, y, w, h; CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight); + int uvRotation = g_Config.iInternalScreenRotation; if (cardboardSettings.enabled) { // Left Eye Image glstate.viewport.set(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); if (applyPostShader && usePostShader_ && useBufferedRendering_) { DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f, postShaderProgram_); } else { - DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f); + DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f); } // Right Eye Image @@ -487,9 +441,9 @@ void FramebufferManager::DrawFramebuffer(const u8 *srcPixels, GEBufferFormat src // Fullscreen Image glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); if (applyPostShader && usePostShader_ && useBufferedRendering_) { - DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f, postShaderProgram_); + DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f, postShaderProgram_, uvRotation); } else { - DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f); + DrawActiveTexture(0, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, false, 0.0f, 0.0f, 480.0f / 512.0f, 1.0f, NULL, uvRotation); } } } @@ -533,15 +487,36 @@ void FramebufferManager::DrawPlainColor(u32 color) { } // x, y, w, h are relative coordinates against destW/destH, which is not very intuitive. -void FramebufferManager::DrawActiveTexture(GLuint texture, float x, float y, float w, float h, float destW, float destH, bool flip, float u0, float v0, float u1, float v1, GLSLProgram *program) { +void FramebufferManager::DrawActiveTexture(GLuint texture, float x, float y, float w, float h, float destW, float destH, bool flip, float u0, float v0, float u1, float v1, GLSLProgram *program, int uvRotation) { if (flip) { // We're flipping, so 0 is downward. Reverse everything from 1.0f. v0 = 1.0f - v0; v1 = 1.0f - v1; } - const float texCoords[8] = {u0,v0, u1,v0, u1,v1, u0,v1}; + + float texCoords[8] = { + u0,v0, + u1,v0, + u1,v1, + u0,v1 + }; + static const GLushort indices[4] = {0,1,3,2}; + if (uvRotation != ROTATION_LOCKED_HORIZONTAL) { + float temp[8]; + int rotation = 0; + switch (uvRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotation = 4; break; + case ROTATION_LOCKED_VERTICAL: rotation = 2; break; + case ROTATION_LOCKED_VERTICAL180: rotation = 6; break; + } + for (int i = 0; i < 8; i++) { + temp[i] = texCoords[(i + rotation) & 7]; + } + memcpy(texCoords, temp, sizeof(temp)); + } + if (texture) { // We know the texture, we can do a DrawTexture shortcut on nvidia. #if defined(ANDROID) @@ -1097,7 +1072,7 @@ void FramebufferManager::CopyDisplayToOutput() { } else { // Fullscreen Image glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); - DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, u0, v0, u1, v1); + DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, u0, v0, u1, v1, NULL, g_Config.iInternalScreenRotation); } } else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) { // An additional pass, post-processing shader to the extra FBO. @@ -1148,7 +1123,7 @@ void FramebufferManager::CopyDisplayToOutput() { } else { // Fullscreen Image glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); - DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, u0, v0, u1, v1, postShaderProgram_); + DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, u0, v0, u1, v1, postShaderProgram_, g_Config.iInternalScreenRotation); } } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index a51e2dbc46..52d1fb7ff1 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -31,6 +31,7 @@ #include "../Globals.h" #include "GPU/GPUCommon.h" #include "GPU/Common/FramebufferCommon.h" +#include "Core/Config.h" struct GLSLProgram; class TextureCache; @@ -86,7 +87,7 @@ public: // If texture != 0, will bind it. // x,y,w,h are relative to destW, destH which fill out the target completely. - void DrawActiveTexture(GLuint texture, float x, float y, float w, float h, float destW, float destH, bool flip = false, float u0 = 0.0f, float v0 = 0.0f, float u1 = 1.0f, float v1 = 1.0f, GLSLProgram *program = 0); + void DrawActiveTexture(GLuint texture, float x, float y, float w, float h, float destW, float destH, bool flip = false, float u0 = 0.0f, float v0 = 0.0f, float u1 = 1.0f, float v1 = 1.0f, GLSLProgram *program = 0, int uvRotation = ROTATION_LOCKED_HORIZONTAL); void DrawPlainColor(u32 color); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index df93a9686a..c9ca6c1554 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -182,6 +182,12 @@ void GameSettingsScreen::CreateViews() { resolutionEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE); resolutionChoice_->SetEnabledPtr(&resolutionEnable_); + // Not sure if we should call this one something different? For now we call it the same as the Android screen rotation option and just put it on the graphics tab. + static const char *screenRotation[] = {"Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed"}; + PopupMultiChoice *rot = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInternalScreenRotation, c->T("Screen Rotation"), screenRotation, 1, ARRAY_SIZE(screenRotation), c, screenManager())); + rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation); + + #ifdef ANDROID static const char *deviceResolutions[] = { "Native device resolution", "Auto (same as Rendering)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" }; int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2;