2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// 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
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-12-29 22:44:35 +00:00
# include <set>
# include <algorithm>
2012-11-01 15:19:01 +00:00
# include "gfx_es2/glsl_program.h"
2012-11-24 14:19:29 +00:00
# include "gfx_es2/gl_state.h"
2013-01-30 20:09:53 +00:00
# include "gfx_es2/fbo.h"
2013-12-02 16:24:20 +00:00
# include "base/timeutil.h"
2012-11-01 15:19:01 +00:00
# include "math/lin/matrix4x4.h"
2013-01-30 20:09:53 +00:00
# include "Core/Host.h"
# include "Core/MemMap.h"
# include "Core/Config.h"
# include "Core/System.h"
2013-09-11 20:21:15 +00:00
# include "Core/Reporting.h"
2014-05-26 18:40:46 +00:00
# include "Core/ELF/ParamSFO.h"
2013-12-02 16:24:20 +00:00
# include "Core/HLE/sceDisplay.h"
2013-01-30 20:09:53 +00:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2012-11-01 15:19:01 +00:00
2013-10-12 00:05:55 +00:00
# include "GPU/Common/PostShader.h"
2014-05-04 07:18:01 +00:00
# include "GPU/Common/TextureDecoder.h"
2013-01-30 20:09:53 +00:00
# include "GPU/GLES/Framebuffer.h"
2013-01-31 23:18:23 +00:00
# include "GPU/GLES/TextureCache.h"
2013-03-15 23:40:37 +00:00
# include "GPU/GLES/ShaderManager.h"
2012-11-01 15:19:01 +00:00
2013-10-13 10:05:50 +00:00
# include "UI/OnScreenDisplay.h"
2013-06-26 21:23:16 +00:00
# if defined(USING_GLES2)
2013-10-08 13:00:48 +00:00
# ifndef GL_READ_FRAMEBUFFER
2013-06-26 21:23:16 +00:00
# define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER
# define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER
2013-10-08 13:00:48 +00:00
# endif
# ifndef GL_RGBA8
2013-06-26 21:23:16 +00:00
# define GL_RGBA8 GL_RGBA
2013-10-08 13:00:48 +00:00
# endif
2013-06-26 21:23:16 +00:00
# ifndef GL_DEPTH_COMPONENT24
# define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
# endif
# ifndef GL_DEPTH24_STENCIL8_OES
# define GL_DEPTH24_STENCIL8_OES 0x88F0
# endif
# endif
2013-06-29 19:35:28 +00:00
extern int g_iNumVideos ;
2013-06-26 21:23:16 +00:00
2013-01-30 20:09:53 +00:00
static const char tex_fs [ ] =
2013-10-20 22:36:23 +00:00
# ifdef USING_GLES2
2012-11-01 15:19:01 +00:00
" precision mediump float; \n "
2013-10-20 22:36:23 +00:00
# endif
2012-11-01 15:19:01 +00:00
" uniform sampler2D sampler0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2013-10-30 13:37:07 +00:00
" gl_FragColor.rgb = texture2D(sampler0, v_texcoord0).rgb; \n "
" gl_FragColor.a = 1.0; \n "
2012-11-01 15:19:01 +00:00
" } \n " ;
2013-01-30 20:09:53 +00:00
static const char basic_vs [ ] =
2012-11-01 15:19:01 +00:00
" attribute vec4 a_position; \n "
" attribute vec2 a_texcoord0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2012-12-21 10:24:38 +00:00
" v_texcoord0 = a_texcoord0; \n "
2013-10-30 13:37:07 +00:00
" gl_Position = a_position; \n "
2012-11-01 15:19:01 +00:00
" } \n " ;
2013-10-30 21:44:01 +00:00
static const char color_fs [ ] =
# ifdef USING_GLES2
" precision mediump float; \n "
# endif
" uniform vec4 u_color; \n "
" void main() { \n "
" gl_FragColor.rgba = u_color; \n "
" } \n " ;
static const char color_vs [ ] =
" attribute vec4 a_position; \n "
" void main() { \n "
" gl_Position = a_position; \n "
" } \n " ;
2013-01-30 20:09:53 +00:00
// Aggressively delete unused FBO:s to save gpu memory.
enum {
2013-04-12 14:00:59 +00:00
FBO_OLD_AGE = 5 ,
2013-01-30 20:09:53 +00:00
} ;
static bool MaskedEqual ( u32 addr1 , u32 addr2 ) {
2013-08-23 06:23:48 +00:00
return ( addr1 & 0x03FFFFFF ) = = ( addr2 & 0x03FFFFFF ) ;
2013-01-30 20:09:53 +00:00
}
2013-07-04 14:24:37 +00:00
inline u16 RGBA8888toRGB565 ( u32 px ) {
2013-06-28 13:48:36 +00:00
return ( ( px > > 3 ) & 0x001F ) | ( ( px > > 5 ) & 0x07E0 ) | ( ( px > > 8 ) & 0xF800 ) ;
}
2013-07-04 14:24:37 +00:00
inline u16 RGBA8888toRGBA4444 ( u32 px ) {
2013-06-28 13:48:36 +00:00
return ( ( px > > 4 ) & 0x000F ) | ( ( px > > 8 ) & 0x00F0 ) | ( ( px > > 12 ) & 0x0F00 ) | ( ( px > > 16 ) & 0xF000 ) ;
}
2014-05-04 07:18:01 +00:00
inline u16 BGRA8888toRGB565 ( u32 px ) {
return ( ( px > > 19 ) & 0x001F ) | ( ( px > > 5 ) & 0x07E0 ) | ( ( px < < 8 ) & 0xF800 ) ;
2013-06-28 13:48:36 +00:00
}
2014-05-04 07:18:01 +00:00
inline u16 BGRA8888toRGBA4444 ( u32 px ) {
return ( ( px > > 20 ) & 0x000F ) | ( ( px > > 8 ) & 0x00F0 ) | ( ( px < < 4 ) & 0x0F00 ) | ( ( px > > 16 ) & 0xF000 ) ;
}
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 stride , u32 height , GEBufferFormat format ) ;
2013-06-28 13:48:36 +00:00
2013-03-15 20:22:17 +00:00
void CenterRect ( float * x , float * y , float * w , float * h ,
2013-10-09 09:01:52 +00:00
float origW , float origH , float frameW , float frameH ) {
2014-01-15 23:17:41 +00:00
float outW ;
float outH ;
2013-10-09 09:01:52 +00:00
if ( g_Config . bStretchToDisplay ) {
2014-01-15 23:17:41 +00:00
outW = frameW ;
outH = frameH ;
} else {
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 ;
}
2013-02-13 17:21:21 +00:00
}
2014-01-15 23:17:41 +00:00
if ( g_Config . bSmallDisplay ) {
outW / = 2.0f ;
outH / = 2.0f ;
2013-02-12 21:09:14 +00:00
}
2014-01-15 23:17:41 +00:00
* x = ( frameW - outW ) / 2.0f ;
* y = ( frameH - outH ) / 2.0f ;
* w = outW ;
* h = outH ;
2013-02-12 21:09:14 +00:00
}
2013-10-22 10:17:40 +00:00
static void ClearBuffer ( ) {
2013-12-06 22:08:37 +00:00
glstate . scissorTest . disable ( ) ;
2013-08-14 19:20:06 +00:00
glstate . depthWrite . set ( GL_TRUE ) ;
glstate . colorMask . set ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2013-12-06 22:08:37 +00:00
glstate . stencilFunc . set ( GL_ALWAYS , 0xFF , 0xFF ) ;
glClearColor ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glClearStencil ( 0xFF ) ;
2014-01-01 22:40:35 +00:00
# ifdef USING_GLES2
glClearDepthf ( 1.0f ) ;
# else
glClearDepth ( 1.0 ) ;
# endif
2013-08-14 19:20:06 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
}
2013-10-22 10:17:40 +00:00
static void DisableState ( ) {
2013-08-14 19:32:43 +00:00
glstate . blend . disable ( ) ;
glstate . cullFace . disable ( ) ;
glstate . depthTest . disable ( ) ;
glstate . scissorTest . disable ( ) ;
glstate . stencilTest . disable ( ) ;
2013-08-23 09:32:10 +00:00
# if !defined(USING_GLES2)
glstate . colorLogicOp . disable ( ) ;
# endif
2013-12-04 13:02:40 +00:00
glstate . colorMask . set ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2013-08-14 19:32:43 +00:00
}
2013-09-28 20:42:13 +00:00
void FramebufferManager : : SetNumExtraFBOs ( int num ) {
2013-09-30 03:28:19 +00:00
for ( size_t i = 0 ; i < extraFBOs_ . size ( ) ; i + + ) {
2013-09-28 20:42:13 +00:00
fbo_destroy ( extraFBOs_ [ i ] ) ;
}
extraFBOs_ . clear ( ) ;
for ( int i = 0 ; i < num ; i + + ) {
// No depth/stencil for post processing
FBO * fbo = fbo_create ( PSP_CoreParameter ( ) . renderWidth , PSP_CoreParameter ( ) . renderHeight , 1 , false , FBO_8888 ) ;
extraFBOs_ . push_back ( fbo ) ;
}
}
2013-07-16 20:50:53 +00:00
void FramebufferManager : : CompileDraw2DProgram ( ) {
2013-09-26 10:41:07 +00:00
if ( ! draw2dprogram_ ) {
2013-10-13 10:05:50 +00:00
std : : string errorString ;
draw2dprogram_ = glsl_create_source ( basic_vs , tex_fs , & errorString ) ;
if ( ! draw2dprogram_ ) {
ERROR_LOG_REPORT ( G3D , " Failed to compile draw2dprogram! This shouldn't happen. \n %s " , errorString . c_str ( ) ) ;
} else {
glsl_bind ( draw2dprogram_ ) ;
glUniform1i ( draw2dprogram_ - > sampler0 , 0 ) ;
}
2013-09-26 10:41:07 +00:00
2013-10-30 21:44:01 +00:00
plainColorProgram_ = glsl_create_source ( color_vs , color_fs , & errorString ) ;
if ( ! plainColorProgram_ ) {
ERROR_LOG_REPORT ( G3D , " Failed to compile plainColorProgram! This shouldn't happen. \n %s " , errorString . c_str ( ) ) ;
} else {
glsl_bind ( plainColorProgram_ ) ;
plainColorLoc_ = glsl_uniform_loc ( plainColorProgram_ , " u_color " ) ;
}
2013-10-09 14:08:36 +00:00
SetNumExtraFBOs ( 0 ) ;
2013-10-12 00:05:55 +00:00
const ShaderInfo * shaderInfo = 0 ;
if ( g_Config . sPostShaderName ! = " Off " ) {
shaderInfo = GetPostShaderInfo ( g_Config . sPostShaderName ) ;
}
if ( shaderInfo ) {
2013-10-22 10:17:40 +00:00
postShaderAtOutputResolution_ = shaderInfo - > outputResolution ;
2013-10-13 10:05:50 +00:00
postShaderProgram_ = glsl_create ( shaderInfo - > vertexShaderFile . c_str ( ) , shaderInfo - > fragmentShaderFile . c_str ( ) , & errorString ) ;
2013-10-12 00:05:55 +00:00
if ( ! postShaderProgram_ ) {
2013-10-13 10:05:50 +00:00
// DO NOT turn this into a report, as it will pollute our logs with all kinds of
// user shader experiments.
ERROR_LOG ( G3D , " Failed to build post-processing program from %s and %s! \n %s " , shaderInfo - > vertexShaderFile . c_str ( ) , shaderInfo - > fragmentShaderFile . c_str ( ) , errorString . c_str ( ) ) ;
// let's show the first line of the error string as an OSM.
2014-02-16 09:25:33 +00:00
std : : set < std : : string > blacklistedLines ;
// These aren't useful to show, skip to the first interesting line.
blacklistedLines . insert ( " Fragment shader failed to compile with the following errors: " ) ;
blacklistedLines . insert ( " Vertex shader failed to compile with the following errors: " ) ;
blacklistedLines . insert ( " Compile failed. " ) ;
blacklistedLines . insert ( " " ) ;
std : : string firstLine ;
size_t start = 0 ;
2013-10-13 16:34:54 +00:00
for ( size_t i = 0 ; i < errorString . size ( ) ; i + + ) {
2013-10-13 10:05:50 +00:00
if ( errorString [ i ] = = ' \n ' ) {
2014-02-16 09:25:33 +00:00
firstLine = errorString . substr ( start , i - start ) ;
if ( blacklistedLines . find ( firstLine ) = = blacklistedLines . end ( ) ) {
break ;
}
start = i + 1 ;
firstLine . clear ( ) ;
2013-10-13 10:05:50 +00:00
}
}
2014-02-16 09:25:33 +00:00
if ( ! firstLine . empty ( ) ) {
osm . Show ( " Post-shader error: " + firstLine + " ... " , 10.0f , 0xFF3090FF ) ;
} else {
osm . Show ( " Post-shader error, see log for details " , 10.0f , 0xFF3090FF ) ;
}
2013-10-12 00:05:55 +00:00
usePostShader_ = false ;
2013-10-09 14:08:36 +00:00
} else {
2013-10-12 00:05:55 +00:00
glsl_bind ( postShaderProgram_ ) ;
glUniform1i ( postShaderProgram_ - > sampler0 , 0 ) ;
2013-10-09 14:08:36 +00:00
SetNumExtraFBOs ( 1 ) ;
float u_delta = 1.0f / PSP_CoreParameter ( ) . renderWidth ;
float v_delta = 1.0f / PSP_CoreParameter ( ) . renderHeight ;
2013-10-22 10:17:40 +00:00
float u_pixel_delta = u_delta ;
float v_pixel_delta = v_delta ;
if ( postShaderAtOutputResolution_ ) {
float x , y , w , h ;
CenterRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight ) ;
u_pixel_delta = 1.0f / w ;
v_pixel_delta = 1.0f / h ;
}
int deltaLoc = glsl_uniform_loc ( postShaderProgram_ , " u_texelDelta " ) ;
2013-12-02 22:35:49 +00:00
if ( deltaLoc ! = - 1 )
glUniform2f ( deltaLoc , u_delta , v_delta ) ;
2013-10-22 10:17:40 +00:00
int pixelDeltaLoc = glsl_uniform_loc ( postShaderProgram_ , " u_pixelDelta " ) ;
2013-12-02 22:35:49 +00:00
if ( pixelDeltaLoc ! = - 1 )
glUniform2f ( pixelDeltaLoc , u_pixel_delta , v_pixel_delta ) ;
2013-12-02 16:24:20 +00:00
timeLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_time " ) ;
2013-12-02 22:35:49 +00:00
if ( timeLoc_ ! = - 1 )
glUniform4f ( timeLoc_ , 0.0f , 0.0f , 0.0f , 0.0f ) ;
2013-12-02 16:24:20 +00:00
2013-10-12 00:05:55 +00:00
usePostShader_ = true ;
2013-10-09 14:08:36 +00:00
}
2013-09-28 20:42:13 +00:00
} else {
2013-10-12 00:05:55 +00:00
postShaderProgram_ = 0 ;
usePostShader_ = false ;
2013-09-26 10:41:07 +00:00
}
2013-07-16 20:50:53 +00:00
glsl_unbind ( ) ;
}
}
2013-09-26 10:41:07 +00:00
void FramebufferManager : : DestroyDraw2DProgram ( ) {
if ( draw2dprogram_ ) {
glsl_destroy ( draw2dprogram_ ) ;
draw2dprogram_ = 0 ;
2013-10-09 14:08:36 +00:00
}
2013-12-02 23:09:48 +00:00
if ( plainColorProgram_ ) {
glsl_destroy ( plainColorProgram_ ) ;
plainColorProgram_ = 0 ;
}
2013-10-12 00:05:55 +00:00
if ( postShaderProgram_ ) {
glsl_destroy ( postShaderProgram_ ) ;
postShaderProgram_ = 0 ;
2013-09-26 10:41:07 +00:00
}
}
2013-01-30 20:09:53 +00:00
FramebufferManager : : FramebufferManager ( ) :
displayFramebufPtr_ ( 0 ) ,
2013-07-06 09:09:08 +00:00
displayStride_ ( 0 ) ,
2013-07-30 06:05:59 +00:00
displayFormat_ ( GE_FORMAT_565 ) ,
2013-07-06 09:09:08 +00:00
displayFramebuf_ ( 0 ) ,
2013-01-30 20:09:53 +00:00
prevDisplayFramebuf_ ( 0 ) ,
2013-01-31 23:18:23 +00:00
prevPrevDisplayFramebuf_ ( 0 ) ,
2013-04-12 14:00:59 +00:00
frameLastFramebufUsed ( 0 ) ,
2013-06-05 21:03:23 +00:00
currentRenderVfb_ ( 0 ) ,
drawPixelsTex_ ( 0 ) ,
2013-07-30 06:05:59 +00:00
drawPixelsTexFormat_ ( GE_FORMAT_INVALID ) ,
2014-05-25 23:28:13 +00:00
convBuf_ ( 0 ) ,
2013-09-28 20:42:13 +00:00
draw2dprogram_ ( 0 ) ,
2013-10-12 00:05:55 +00:00
postShaderProgram_ ( 0 ) ,
2013-10-30 23:07:33 +00:00
plainColorLoc_ ( - 1 ) ,
2013-12-02 16:24:20 +00:00
timeLoc_ ( - 1 ) ,
2013-09-28 20:42:13 +00:00
textureCache_ ( 0 ) ,
shaderManager_ ( 0 ) ,
2014-01-11 06:13:11 +00:00
usePostShader_ ( false ) ,
postShaderAtOutputResolution_ ( false ) ,
2014-05-26 00:01:28 +00:00
resized_ ( false ) ,
2014-05-26 01:18:26 +00:00
gameUsesSequentialCopies_ ( false ) ,
2014-05-26 00:01:28 +00:00
framebufRangeEnd_ ( 0 )
2013-07-05 22:40:01 +00:00
# ifndef USING_GLES2
,
2013-06-28 13:48:36 +00:00
pixelBufObj_ ( 0 ) ,
currentPBO_ ( 0 )
2013-07-05 22:40:01 +00:00
# endif
2013-01-30 20:09:53 +00:00
{
2013-07-16 20:50:53 +00:00
CompileDraw2DProgram ( ) ;
2012-11-01 15:19:01 +00:00
2012-11-18 12:04:49 +00:00
// And an initial clear. We don't clear per frame as the games are supposed to handle that
// by themselves.
2013-08-14 19:32:43 +00:00
ClearBuffer ( ) ;
2013-01-14 18:26:10 +00:00
2013-08-14 19:20:06 +00:00
useBufferedRendering_ = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
2014-05-09 08:09:56 +00:00
updateVRAM_ = ! ( g_Config . iRenderingMode = = FB_NON_BUFFERED_MODE | | g_Config . iRenderingMode = = FB_BUFFERED_MODE ) ;
2014-05-26 18:40:46 +00:00
const std : : string gameId = g_paramSFO . GetValueString ( " DISC_ID " ) ;
// This applies a hack to Dangan Ronpa, its demo, and its sequel.
// The game draws solid colors to a small framebuffer, and then reads this directly in VRAM.
// We force this framebuffer to 1x and force download it automatically.
hackForce04154000Download_ = gameId = = " NPJH50631 " | | gameId = = " NPJH50372 " | | gameId = = " NPJH90164 " ;
2013-10-09 17:00:35 +00:00
SetLineWidth ( ) ;
2012-11-01 15:19:01 +00:00
}
2012-12-01 01:15:46 +00:00
FramebufferManager : : ~ FramebufferManager ( ) {
2013-06-05 21:03:23 +00:00
if ( drawPixelsTex_ )
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
2013-09-26 10:41:07 +00:00
if ( draw2dprogram_ ) {
glsl_destroy ( draw2dprogram_ ) ;
2013-07-16 20:50:53 +00:00
}
2013-09-28 20:42:13 +00:00
SetNumExtraFBOs ( 0 ) ;
2013-06-28 13:48:36 +00:00
2014-01-20 10:12:44 +00:00
for ( auto it = renderCopies_ . begin ( ) , end = renderCopies_ . end ( ) ; it ! = end ; + + it ) {
fbo_destroy ( it - > second ) ;
}
2014-01-20 02:27:52 +00:00
2013-07-05 22:40:01 +00:00
# ifndef USING_GLES2
2013-06-28 13:48:36 +00:00
delete [ ] pixelBufObj_ ;
2013-07-05 22:40:01 +00:00
# endif
2014-05-25 23:28:13 +00:00
delete [ ] convBuf_ ;
2012-11-01 15:19:01 +00:00
}
2014-05-09 20:26:42 +00:00
void FramebufferManager : : MakePixelTexture ( const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , int width , int height ) {
if ( drawPixelsTex_ & & ( drawPixelsTexFormat_ ! = srcPixelFormat | | drawPixelsTexW_ ! = width | | drawPixelsTexH_ ! = height ) ) {
2013-06-05 21:03:23 +00:00
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
drawPixelsTex_ = 0 ;
}
if ( ! drawPixelsTex_ ) {
glGenTextures ( 1 , & drawPixelsTex_ ) ;
2014-05-09 20:26:42 +00:00
drawPixelsTexW_ = width ;
drawPixelsTexH_ = height ;
2013-06-05 21:03:23 +00:00
// Initialize backbuffer texture for DrawPixels
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
2014-05-09 21:11:04 +00:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2014-05-09 20:26:42 +00:00
drawPixelsTexFormat_ = srcPixelFormat ;
2014-05-09 21:11:04 +00:00
} else {
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
2013-06-05 21:03:23 +00:00
}
2013-06-05 21:26:51 +00:00
// TODO: We can just change the texture format and flip some bits around instead of this.
2014-05-09 21:11:04 +00:00
// Could share code with the texture cache perhaps.
2013-10-21 02:55:01 +00:00
bool useConvBuf = false ;
2014-05-09 21:11:04 +00:00
if ( srcPixelFormat ! = GE_FORMAT_8888 | | srcStride ! = width ) {
2013-10-21 02:55:01 +00:00
useConvBuf = true ;
2014-05-25 23:28:13 +00:00
u32 neededSize = width * height * 4 ;
if ( ! convBuf_ | | convBufSize_ < neededSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ neededSize ] ;
convBufSize_ = neededSize ;
2013-06-05 21:26:51 +00:00
}
2014-05-09 21:11:04 +00:00
for ( int y = 0 ; y < height ; y + + ) {
2014-05-09 20:26:42 +00:00
switch ( srcPixelFormat ) {
2013-07-30 05:58:37 +00:00
case GE_FORMAT_565 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 23:28:13 +00:00
u8 * dst = convBuf_ + 4 * width * y ;
2014-05-09 21:11:04 +00:00
for ( int x = 0 ; x < width ; x + + )
2013-06-05 21:26:51 +00:00
{
u16 col = src [ x ] ;
dst [ x * 4 ] = ( ( col ) & 0x1f ) < < 3 ;
dst [ x * 4 + 1 ] = ( ( col > > 5 ) & 0x3f ) < < 2 ;
dst [ x * 4 + 2 ] = ( ( col > > 11 ) & 0x1f ) < < 3 ;
dst [ x * 4 + 3 ] = 255 ;
}
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_5551 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 23:28:13 +00:00
u8 * dst = convBuf_ + 4 * width * y ;
2014-05-09 21:11:04 +00:00
for ( int x = 0 ; x < width ; x + + )
2013-06-05 21:26:51 +00:00
{
u16 col = src [ x ] ;
dst [ x * 4 ] = ( ( col ) & 0x1f ) < < 3 ;
dst [ x * 4 + 1 ] = ( ( col > > 5 ) & 0x1f ) < < 3 ;
dst [ x * 4 + 2 ] = ( ( col > > 10 ) & 0x1f ) < < 3 ;
dst [ x * 4 + 3 ] = ( col > > 15 ) ? 255 : 0 ;
}
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_4444 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 23:28:13 +00:00
u8 * dst = convBuf_ + 4 * width * y ;
2014-05-09 21:11:04 +00:00
for ( int x = 0 ; x < width ; x + + )
2013-06-05 21:26:51 +00:00
{
u16 col = src [ x ] ;
dst [ x * 4 ] = ( ( col > > 8 ) & 0xf ) < < 4 ;
dst [ x * 4 + 1 ] = ( ( col > > 4 ) & 0xf ) < < 4 ;
dst [ x * 4 + 2 ] = ( col & 0xf ) < < 4 ;
dst [ x * 4 + 3 ] = ( col > > 12 ) < < 4 ;
}
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_8888 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u8 * src = srcPixels + srcStride * 4 * y ;
2014-05-25 23:28:13 +00:00
u8 * dst = convBuf_ + 4 * width * y ;
2014-05-09 21:11:04 +00:00
memcpy ( dst , src , 4 * width ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2013-08-13 06:40:22 +00:00
case GE_FORMAT_INVALID :
_dbg_assert_msg_ ( G3D , false , " Invalid pixelFormat passed to DrawPixels(). " ) ;
break ;
2012-11-01 15:19:01 +00:00
}
}
}
2014-05-25 23:28:13 +00:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , width , height , GL_RGBA , GL_UNSIGNED_BYTE , useConvBuf ? convBuf_ : srcPixels ) ;
2014-05-09 20:26:42 +00:00
}
void FramebufferManager : : DrawPixels ( VirtualFramebuffer * vfb , int dstX , int dstY , const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , int width , int height ) {
MakePixelTexture ( srcPixels , srcPixelFormat , srcStride , width , height ) ;
2014-05-09 21:11:04 +00:00
DisableState ( ) ;
2014-05-09 20:26:42 +00:00
DrawActiveTexture ( 0 , dstX , dstY , width , height , vfb - > width , vfb - > height , false , 0.0f , 0.0f , 1.0f , 1.0f ) ;
}
void FramebufferManager : : DrawFramebuffer ( const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , bool applyPostShader ) {
MakePixelTexture ( srcPixels , srcPixelFormat , srcStride , 512 , 272 ) ;
2014-05-09 21:11:04 +00:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2013-02-12 21:09:14 +00:00
2013-12-04 13:02:40 +00:00
DisableState ( ) ;
2013-12-04 10:46:42 +00:00
// This might draw directly at the backbuffer (if so, applyPostShader is set) so if there's a post shader, we need to apply it here.
// Should try to unify this path with the regular path somehow, but this simple solution works for most of the post shaders
// (it always runs at output resolution so FXAA may look odd).
2014-05-09 21:11:04 +00:00
float x , y , w , h ;
CenterRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight ) ;
2013-12-21 08:18:38 +00:00
if ( applyPostShader & & usePostShader_ & & g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ) {
2014-05-08 13:28:25 +00:00
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_ ) ;
2013-10-22 10:17:40 +00:00
} else {
2014-05-08 13:28:25 +00:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight , false , 0.0f , 0.0f , 480.0f / 512.0f ) ;
2013-10-22 10:17:40 +00:00
}
2012-11-19 22:29:14 +00:00
}
2013-10-30 21:44:01 +00:00
void FramebufferManager : : DrawPlainColor ( u32 color ) {
// Cannot take advantage of scissor + clear here - this has to be a regular draw so that
// stencil can be used and abused, as that's what we're gonna use this for.
static const float pos [ 12 ] = {
- 1 , - 1 , - 1 ,
1 , - 1 , - 1 ,
1 , 1 , - 1 ,
- 1 , 1 , - 1
} ;
static const GLubyte indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
GLSLProgram * program = 0 ;
if ( ! draw2dprogram_ ) {
CompileDraw2DProgram ( ) ;
}
program = plainColorProgram_ ;
const float col [ 4 ] = {
( ( color & 0xFF ) ) / 255.0f ,
( ( color & 0xFF00 ) > > 8 ) / 255.0f ,
( ( color & 0xFF0000 ) > > 16 ) / 255.0f ,
( ( color & 0xFF000000 ) > > 24 ) / 255.0f ,
} ;
glsl_bind ( program ) ;
glUniform4fv ( plainColorLoc_ , 1 , col ) ;
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
glEnableVertexAttribArray ( program - > a_position ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , pos ) ;
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , indices ) ;
glDisableVertexAttribArray ( program - > a_position ) ;
glsl_unbind ( ) ;
}
2014-05-08 13:16:33 +00:00
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
2014-05-08 13:28:25 +00:00
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 ) {
if ( flip ) {
2014-05-10 16:35:02 +00:00
// We're flipping, so 0 is downward. Reverse everything from 1.0f.
v0 = 1.0f - v0 ;
v1 = 1.0f - v1 ;
2014-05-08 13:28:25 +00:00
}
const float texCoords [ 8 ] = { u0 , v0 , u1 , v0 , u1 , v1 , u0 , v1 } ;
2013-12-06 12:01:34 +00:00
static const GLushort indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
2013-10-22 10:17:40 +00:00
if ( texture ) {
// We know the texture, we can do a DrawTexture shortcut on nvidia.
2013-12-03 19:13:56 +00:00
# if !defined(__SYMBIAN32__) && !defined(MEEGO_EDITION_HARMATTAN) && !defined(IOS) && !defined(BLACKBERRY) && !defined(MAEMO)
2014-05-08 13:28:25 +00:00
// Don't remember why I disabled this - no win?
2013-12-06 12:01:34 +00:00
if ( false & & gl_extensions . NV_draw_texture & & ! program ) {
2013-10-23 12:46:04 +00:00
// Fast path for Tegra. TODO: Make this path work on desktop nvidia, seems GLEW doesn't have a clue.
// Actually, on Desktop we should just use glBlitFramebuffer - although we take a texture here
// so that's a little gnarly, will have to modify all callers.
2013-10-22 10:17:40 +00:00
glDrawTextureNV ( texture , 0 ,
x , y , w , h , 0.0f ,
2014-05-08 13:28:25 +00:00
u0 , v1 , u1 , v0 ) ;
2013-10-22 10:17:40 +00:00
return ;
}
# endif
glBindTexture ( GL_TEXTURE_2D , texture ) ;
}
2013-10-30 13:37:07 +00:00
float pos [ 12 ] = {
x , y , 0 ,
x + w , y , 0 ,
x + w , y + h , 0 ,
x , y + h , 0
} ;
2014-05-08 13:28:25 +00:00
float invDestW = 1.0f / ( destW * 0.5f ) ;
float invDestH = 1.0f / ( destH * 0.5f ) ;
2013-10-30 13:37:07 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2014-05-08 13:28:25 +00:00
pos [ i * 3 ] = pos [ i * 3 ] * invDestW - 1.0f ;
pos [ i * 3 + 1 ] = - ( pos [ i * 3 + 1 ] * invDestH - 1.0f ) ;
2013-10-30 13:37:07 +00:00
}
2013-09-28 20:42:13 +00:00
if ( ! program ) {
2013-10-22 13:54:53 +00:00
if ( ! draw2dprogram_ ) {
CompileDraw2DProgram ( ) ;
}
2013-09-26 10:41:07 +00:00
program = draw2dprogram_ ;
2013-06-28 13:48:36 +00:00
}
2013-12-16 15:04:08 +00:00
// Always use linear filtering when stretching a buffer to the screen. Might want to make this
// an option in the future.
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
2013-06-28 13:48:36 +00:00
glsl_bind ( program ) ;
2013-12-02 16:24:20 +00:00
if ( program = = postShaderProgram_ & & timeLoc_ ! = - 1 ) {
int flipCount = __DisplayGetFlipCount ( ) ;
int vCount = __DisplayGetVCount ( ) ;
float time [ 4 ] = { time_now ( ) , ( vCount % 60 ) * 1.0f / 60.0f , ( float ) vCount , ( float ) ( flipCount % 60 ) } ;
glUniform4fv ( timeLoc_ , 1 , time ) ;
}
2013-01-10 22:49:33 +00:00
glBindBuffer ( GL_ARRAY_BUFFER , 0 ) ;
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER , 0 ) ;
2013-06-28 13:48:36 +00:00
glEnableVertexAttribArray ( program - > a_position ) ;
glEnableVertexAttribArray ( program - > a_texcoord0 ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , pos ) ;
2013-07-01 20:51:24 +00:00
glVertexAttribPointer ( program - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , texCoords ) ;
2013-12-06 12:01:34 +00:00
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_SHORT , indices ) ;
2013-06-28 13:48:36 +00:00
glDisableVertexAttribArray ( program - > a_position ) ;
glDisableVertexAttribArray ( program - > a_texcoord0 ) ;
2013-10-10 18:11:20 +00:00
2012-11-01 15:19:01 +00:00
glsl_unbind ( ) ;
2013-10-10 18:11:20 +00:00
shaderManager_ - > DirtyLastShader ( ) ; // dirty lastShader_
2012-11-01 15:19:01 +00:00
}
2013-01-30 20:09:53 +00:00
2013-09-21 16:52:30 +00:00
VirtualFramebuffer * FramebufferManager : : GetVFBAt ( u32 addr ) {
2013-05-04 22:28:57 +00:00
VirtualFramebuffer * match = NULL ;
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * v = vfbs_ [ i ] ;
2013-09-21 16:52:30 +00:00
if ( MaskedEqual ( v - > fb_address , addr ) & & v - > format = = displayFormat_ & & v - > width > = 480 ) {
2013-01-30 20:09:53 +00:00
// Could check w too but whatever
2013-09-01 18:55:03 +00:00
if ( match = = NULL | | match - > last_frame_render < v - > last_frame_render ) {
2013-05-04 22:28:57 +00:00
match = v ;
}
2013-01-30 20:09:53 +00:00
}
}
2013-05-04 22:28:57 +00:00
if ( match ! = NULL ) {
return match ;
}
2013-09-21 16:52:30 +00:00
DEBUG_LOG ( SCEGE , " Finding no FBO matching address %08x " , addr ) ;
2013-01-30 20:09:53 +00:00
return 0 ;
}
2013-03-15 23:40:37 +00:00
// Heuristics to figure out the size of FBO to create.
2013-10-23 12:46:04 +00:00
static void EstimateDrawingSize ( int & drawing_width , int & drawing_height ) {
int default_width = 480 ;
2013-07-15 13:49:43 +00:00
int default_height = 272 ;
2013-10-23 12:46:04 +00:00
int viewport_width = ( int ) gstate . getViewportX1 ( ) ;
int viewport_height = ( int ) gstate . getViewportY1 ( ) ;
2013-08-24 07:59:50 +00:00
int region_width = gstate . getRegionX2 ( ) + 1 ;
int region_height = gstate . getRegionY2 ( ) + 1 ;
int scissor_width = gstate . getScissorX2 ( ) + 1 ;
int scissor_height = gstate . getScissorY2 ( ) + 1 ;
2013-12-15 17:48:44 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2013-08-10 09:22:22 +00:00
2013-09-11 21:29:26 +00:00
DEBUG_LOG ( SCEGE , " viewport : %ix%i, region : %ix%i , scissor: %ix%i, stride: %i, %i " , viewport_width , viewport_height , region_width , region_height , scissor_width , scissor_height , fb_stride , gstate . isModeThrough ( ) ) ;
2013-08-10 09:22:22 +00:00
2014-05-11 11:07:43 +00:00
// Viewport may return 0x0 for example FF Type-0 / God of War and we set it to 480x272
2013-08-10 09:22:22 +00:00
if ( viewport_width < = 1 & & viewport_height < = 1 ) {
2013-08-24 07:59:50 +00:00
viewport_width = default_width ;
viewport_height = default_height ;
2013-10-23 12:46:04 +00:00
}
2013-08-24 07:59:50 +00:00
2014-05-11 11:07:43 +00:00
if ( fb_stride > 0 & & fb_stride < = 512 ) {
if ( fb_stride = = viewport_width ) {
2013-09-14 13:03:24 +00:00
drawing_width = viewport_width ;
drawing_height = viewport_height ;
2013-08-24 07:59:50 +00:00
} else {
2014-05-11 11:07:43 +00:00
drawing_width = scissor_width ;
drawing_height = scissor_height ;
2013-08-10 09:22:22 +00:00
}
2014-05-11 11:07:43 +00:00
} else {
drawing_width = default_width ;
drawing_height = default_height ;
2013-07-15 13:49:43 +00:00
}
2014-05-11 11:07:43 +00:00
2013-01-31 23:18:23 +00:00
}
2013-06-23 15:16:22 +00:00
void FramebufferManager : : DestroyFramebuf ( VirtualFramebuffer * v ) {
2013-08-01 06:19:15 +00:00
textureCache_ - > NotifyFramebuffer ( v - > fb_address , v , NOTIFY_FB_DESTROYED ) ;
2013-06-23 15:16:22 +00:00
if ( v - > fbo ) {
fbo_destroy ( v - > fbo ) ;
v - > fbo = 0 ;
}
2013-06-23 15:05:59 +00:00
// Wipe some pointers
if ( currentRenderVfb_ = = v )
currentRenderVfb_ = 0 ;
if ( displayFramebuf_ = = v )
displayFramebuf_ = 0 ;
if ( prevDisplayFramebuf_ = = v )
prevDisplayFramebuf_ = 0 ;
if ( prevPrevDisplayFramebuf_ = = v )
prevPrevDisplayFramebuf_ = 0 ;
2013-06-23 15:16:22 +00:00
delete v ;
2013-06-23 15:05:59 +00:00
}
2014-03-30 04:51:53 +00:00
void FramebufferManager : : DoSetRenderFrameBuffer ( ) {
2013-12-03 19:13:56 +00:00
/*
2013-12-21 08:18:38 +00:00
if ( g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE & & currentRenderVfb_ ) {
2013-10-30 21:44:01 +00:00
// 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_
}
2013-12-03 19:13:56 +00:00
*/
2013-10-30 21:44:01 +00:00
2013-04-21 22:08:47 +00:00
gstate_c . framebufChanged = false ;
2013-01-30 20:09:53 +00:00
// Get parameters
2013-10-06 22:51:31 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2013-01-30 20:09:53 +00:00
2013-10-06 22:51:31 +00:00
u32 z_address = gstate . getDepthBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int z_stride = gstate . DepthBufStride ( ) ;
2013-01-30 20:09:53 +00:00
// Yeah this is not completely right. but it'll do for now.
2013-07-07 02:39:22 +00:00
//int drawing_width = ((gstate.region2) & 0x3FF) + 1;
//int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1;
2013-10-23 12:46:04 +00:00
2013-08-24 17:20:07 +00:00
GEBufferFormat fmt = gstate . FrameBufFormat ( ) ;
2013-01-30 20:09:53 +00:00
2013-10-23 12:46:04 +00:00
// As there are no clear "framebuffer width" and "framebuffer height" registers,
// we need to infer the size of the current framebuffer somehow.
2013-07-07 02:39:22 +00:00
int drawing_width , drawing_height ;
2013-10-23 12:46:04 +00:00
EstimateDrawingSize ( drawing_width , drawing_height ) ;
2013-03-15 23:40:37 +00:00
2013-06-20 20:18:44 +00:00
int buffer_width = drawing_width ;
int buffer_height = drawing_height ;
2013-11-01 14:00:34 +00:00
// Find a matching framebuffer
2013-01-30 20:09:53 +00:00
VirtualFramebuffer * vfb = 0 ;
2014-04-13 06:08:04 +00:00
size_t i ;
for ( i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
2013-06-23 15:51:35 +00:00
VirtualFramebuffer * v = vfbs_ [ i ] ;
2013-11-10 02:38:33 +00:00
if ( MaskedEqual ( v - > fb_address , fb_address ) ) {
2013-11-07 14:27:21 +00:00
vfb = v ;
// Update fb stride in case it changed
vfb - > fb_stride = fb_stride ;
2013-11-14 11:33:20 +00:00
if ( v - > width < drawing_width & & v - > height < drawing_height ) {
2014-04-13 06:08:04 +00:00
v - > width = drawing_width ;
v - > height = drawing_height ;
2013-11-14 11:33:20 +00:00
}
2013-11-10 02:38:33 +00:00
if ( v - > format ! = fmt ) {
v - > width = drawing_width ;
v - > height = drawing_height ;
v - > format = fmt ;
}
2013-11-07 14:27:21 +00:00
break ;
}
2013-01-30 20:09:53 +00:00
}
2014-04-13 15:15:08 +00:00
if ( vfb ) {
if ( ( drawing_width ! = vfb - > bufferWidth | | drawing_height ! = vfb - > bufferHeight ) ) {
// If it's newly wrong, or changing every frame, just keep track.
if ( vfb - > newWidth ! = drawing_width | | vfb - > newHeight ! = drawing_height ) {
vfb - > newWidth = drawing_width ;
vfb - > newHeight = drawing_height ;
vfb - > lastFrameNewSize = gpuStats . numFlips ;
} else if ( vfb - > lastFrameNewSize + FBO_OLD_AGE < gpuStats . numFlips ) {
// Okay, it's changed for a while (and stayed that way.) Let's start over.
DestroyFramebuf ( vfb ) ;
vfbs_ . erase ( vfbs_ . begin ( ) + i ) ;
vfb = NULL ;
}
} else {
// It's not different, let's keep track of that too.
2014-04-13 06:08:04 +00:00
vfb - > lastFrameNewSize = gpuStats . numFlips ;
}
}
2013-01-31 23:18:23 +00:00
float renderWidthFactor = ( float ) PSP_CoreParameter ( ) . renderWidth / 480.0f ;
float renderHeightFactor = ( float ) PSP_CoreParameter ( ) . renderHeight / 272.0f ;
2014-05-26 18:40:46 +00:00
if ( hackForce04154000Download_ & & MaskedEqual ( fb_address , 0x04154000 ) ) {
renderWidthFactor = 1.0 ;
renderHeightFactor = 1.0 ;
}
2013-01-30 20:09:53 +00:00
// None found? Create one.
if ( ! vfb ) {
2014-04-13 21:45:55 +00:00
gstate_c . textureChanged | = TEXCHANGE_PARAMSONLY ;
2013-01-30 20:09:53 +00:00
vfb = new VirtualFramebuffer ( ) ;
2013-04-09 22:06:29 +00:00
vfb - > fbo = 0 ;
2013-01-30 20:09:53 +00:00
vfb - > fb_address = fb_address ;
vfb - > fb_stride = fb_stride ;
vfb - > z_address = z_address ;
vfb - > z_stride = z_stride ;
vfb - > width = drawing_width ;
vfb - > height = drawing_height ;
2014-04-13 06:08:04 +00:00
vfb - > newWidth = drawing_width ;
vfb - > newHeight = drawing_height ;
vfb - > lastFrameNewSize = gpuStats . numFlips ;
2013-02-10 11:13:35 +00:00
vfb - > renderWidth = ( u16 ) ( drawing_width * renderWidthFactor ) ;
vfb - > renderHeight = ( u16 ) ( drawing_height * renderHeightFactor ) ;
2013-06-20 20:18:44 +00:00
vfb - > bufferWidth = buffer_width ;
vfb - > bufferHeight = buffer_height ;
2013-01-30 20:09:53 +00:00
vfb - > format = fmt ;
2013-02-21 20:37:19 +00:00
vfb - > usageFlags = FB_USAGE_RENDERTARGET ;
2013-03-03 12:00:21 +00:00
vfb - > dirtyAfterDisplay = true ;
2013-08-15 23:00:26 +00:00
if ( ( gstate_c . skipDrawReason & SKIPDRAW_SKIPFRAME ) = = 0 )
vfb - > reallyDirtyAfterDisplay = true ;
2014-01-24 09:44:24 +00:00
vfb - > memoryUpdated = false ;
vfb - > depthUpdated = false ;
2013-01-30 20:09:53 +00:00
2013-03-09 05:46:11 +00:00
if ( g_Config . bTrueColor ) {
2013-02-19 18:07:42 +00:00
vfb - > colorDepth = FBO_8888 ;
2013-03-09 05:46:11 +00:00
} else {
switch ( fmt ) {
2013-07-30 06:05:59 +00:00
case GE_FORMAT_4444 :
vfb - > colorDepth = FBO_4444 ;
2013-03-09 05:46:11 +00:00
break ;
2013-07-30 06:05:59 +00:00
case GE_FORMAT_5551 :
vfb - > colorDepth = FBO_5551 ;
2013-03-09 05:46:11 +00:00
break ;
2013-07-30 06:05:59 +00:00
case GE_FORMAT_565 :
vfb - > colorDepth = FBO_565 ;
2013-03-09 05:46:11 +00:00
break ;
2013-07-30 06:05:59 +00:00
case GE_FORMAT_8888 :
default :
vfb - > colorDepth = FBO_8888 ;
2013-03-09 05:46:11 +00:00
break ;
}
}
2013-10-23 12:46:04 +00:00
2013-04-27 18:06:31 +00:00
if ( useBufferedRendering_ ) {
2013-03-03 12:00:21 +00:00
vfb - > fbo = fbo_create ( vfb - > renderWidth , vfb - > renderHeight , 1 , true , vfb - > colorDepth ) ;
2013-06-23 15:05:59 +00:00
if ( vfb - > fbo ) {
fbo_bind_as_render_target ( vfb - > fbo ) ;
} else {
2013-09-07 20:02:55 +00:00
ERROR_LOG ( SCEGE , " Error creating FBO! %i x %i " , vfb - > renderWidth , vfb - > renderHeight ) ;
2013-06-23 15:05:59 +00:00
}
2013-03-09 05:46:11 +00:00
} else {
fbo_unbind ( ) ;
// Let's ignore rendering to targets that have not (yet) been displayed.
gstate_c . skipDrawReason | = SKIPDRAW_NON_DISPLAYED_FB ;
2013-03-03 12:00:21 +00:00
}
2013-08-01 06:19:15 +00:00
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_CREATED ) ;
2013-01-30 20:09:53 +00:00
2013-09-01 18:55:03 +00:00
vfb - > last_frame_render = gpuStats . numFlips ;
2013-08-07 20:32:04 +00:00
frameLastFramebufUsed = gpuStats . numFlips ;
2013-01-30 20:09:53 +00:00
vfbs_ . push_back ( vfb ) ;
2013-08-14 19:32:43 +00:00
ClearBuffer ( ) ;
2013-12-06 22:08:37 +00:00
glEnable ( GL_DITHER ) ; // why?
2013-01-30 20:09:53 +00:00
currentRenderVfb_ = vfb ;
2013-03-09 05:46:11 +00:00
2014-05-26 00:12:58 +00:00
u32 byteSize = FramebufferByteSize ( vfb ) ;
2014-05-26 00:51:12 +00:00
u32 fb_address_mem = ( fb_address & 0x3FFFFFFF ) | 0x04000000 ;
if ( fb_address_mem + byteSize > framebufRangeEnd_ ) {
framebufRangeEnd_ = fb_address_mem + byteSize ;
2014-05-26 00:01:28 +00:00
}
2013-09-07 20:02:55 +00:00
INFO_LOG ( SCEGE , " Creating FBO for %08x : %i x %i x %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format ) ;
2013-01-30 20:09:53 +00:00
2013-09-23 04:44:11 +00:00
// Let's check for depth buffer overlap. Might be interesting.
bool sharingReported = false ;
for ( size_t i = 0 , end = vfbs_ . size ( ) ; i < end ; + + i ) {
if ( MaskedEqual ( fb_address , vfbs_ [ i ] - > z_address ) ) {
2013-12-08 08:38:10 +00:00
// If it's clearing it, most likely it just needs more video memory.
// Technically it could write something interesting and the other might not clear, but that's not likely.
if ( ! gstate . isModeClear ( ) | | ! gstate . isClearModeColorMask ( ) | | ! gstate . isClearModeAlphaMask ( ) ) {
WARN_LOG_REPORT ( SCEGE , " FBO created from existing depthbuffer as color, %08x/%08x and %08x/%08x " , fb_address , z_address , vfbs_ [ i ] - > fb_address , vfbs_ [ i ] - > z_address ) ;
}
2013-09-23 04:44:11 +00:00
} else if ( MaskedEqual ( z_address , vfbs_ [ i ] - > fb_address ) ) {
2013-12-08 08:38:10 +00:00
// If it's clearing it, then it's probably just the reverse of the above case.
if ( ! gstate . isModeClear ( ) | | ! gstate . isClearModeDepthMask ( ) ) {
WARN_LOG_REPORT ( SCEGE , " FBO using existing buffer as depthbuffer, %08x/%08x and %08x/%08x " , fb_address , z_address , vfbs_ [ i ] - > fb_address , vfbs_ [ i ] - > z_address ) ;
}
2013-09-23 04:44:11 +00:00
} else if ( MaskedEqual ( z_address , vfbs_ [ i ] - > z_address ) & & fb_address ! = vfbs_ [ i ] - > fb_address & & ! sharingReported ) {
2013-12-08 08:38:10 +00:00
// This happens a lot, but virtually always it's cleared.
// It's possible the other might not clear, but when every game is reported it's not useful.
if ( ! gstate . isModeClear ( ) | | ! gstate . isClearModeDepthMask ( ) ) {
WARN_LOG_REPORT ( SCEGE , " FBO reusing depthbuffer, %08x/%08x and %08x/%08x " , fb_address , z_address , vfbs_ [ i ] - > fb_address , vfbs_ [ i ] - > z_address ) ;
sharingReported = true ;
}
2013-09-23 04:44:11 +00:00
}
}
2013-03-03 12:00:21 +00:00
// We already have it!
} else if ( vfb ! = currentRenderVfb_ ) {
2014-05-09 08:09:56 +00:00
2014-05-26 18:40:46 +00:00
if ( ShouldDownloadFramebuffer ( vfb ) & & ! vfb - > memoryUpdated ) {
2014-05-09 11:42:34 +00:00
ReadFramebufferToMemory ( vfb , true , 0 , 0 , vfb - > width , vfb - > height ) ;
2013-10-23 12:46:04 +00:00
}
2013-01-30 20:09:53 +00:00
// Use it as a render target.
2013-09-07 20:02:55 +00:00
DEBUG_LOG ( SCEGE , " Switching render target to FBO for %08x: %i x %i x %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format ) ;
2013-03-03 12:00:21 +00:00
vfb - > usageFlags | = FB_USAGE_RENDERTARGET ;
2014-04-13 21:45:55 +00:00
gstate_c . textureChanged | = TEXCHANGE_PARAMSONLY ;
2013-09-01 18:55:03 +00:00
vfb - > last_frame_render = gpuStats . numFlips ;
2013-08-07 20:32:04 +00:00
frameLastFramebufUsed = gpuStats . numFlips ;
2013-03-03 12:00:21 +00:00
vfb - > dirtyAfterDisplay = true ;
2013-08-15 23:00:26 +00:00
if ( ( gstate_c . skipDrawReason & SKIPDRAW_SKIPFRAME ) = = 0 )
vfb - > reallyDirtyAfterDisplay = true ;
2013-08-11 18:30:17 +00:00
vfb - > memoryUpdated = false ;
2013-03-03 12:00:21 +00:00
2013-04-27 18:06:31 +00:00
if ( useBufferedRendering_ ) {
2013-04-09 22:06:29 +00:00
if ( vfb - > fbo ) {
fbo_bind_as_render_target ( vfb - > fbo ) ;
} else {
// wtf? This should only happen very briefly when toggling bBufferedRendering
fbo_unbind ( ) ;
}
2013-03-03 12:00:21 +00:00
} else {
2013-04-09 22:06:29 +00:00
if ( vfb - > fbo ) {
// wtf? This should only happen very briefly when toggling bBufferedRendering
2013-08-01 06:19:15 +00:00
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_DESTROYED ) ;
2013-04-09 22:06:29 +00:00
fbo_destroy ( vfb - > fbo ) ;
vfb - > fbo = 0 ;
}
2013-03-03 12:00:21 +00:00
fbo_unbind ( ) ;
// Let's ignore rendering to targets that have not (yet) been displayed.
2013-10-23 12:46:04 +00:00
if ( vfb - > usageFlags & FB_USAGE_DISPLAYED_FRAMEBUFFER ) {
2013-03-03 12:00:21 +00:00
gstate_c . skipDrawReason & = ~ SKIPDRAW_NON_DISPLAYED_FB ;
} else {
2013-10-23 12:46:04 +00:00
gstate_c . skipDrawReason | = SKIPDRAW_NON_DISPLAYED_FB ;
}
2013-03-03 12:00:21 +00:00
}
2013-08-01 06:19:15 +00:00
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_UPDATED ) ;
2013-02-28 22:47:57 +00:00
2013-01-30 20:09:53 +00:00
# ifdef USING_GLES2
2013-02-01 23:24:23 +00:00
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
// to it. This broke stuff before, so now it only clears on the first use of an
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
2013-09-01 18:55:03 +00:00
if ( vfb - > last_frame_render ! = gpuStats . numFlips ) {
2013-08-14 20:14:18 +00:00
ClearBuffer ( ) ;
2013-02-22 21:14:17 +00:00
}
2013-01-30 20:09:53 +00:00
# endif
2014-01-04 19:23:23 +00:00
2014-01-20 14:02:20 +00:00
// Copy depth pixel value from the read framebuffer to the draw framebuffer
BindFramebufferDepth ( currentRenderVfb_ , vfb ) ;
2013-01-30 20:09:53 +00:00
currentRenderVfb_ = vfb ;
2013-03-15 23:40:37 +00:00
} else {
2013-09-01 18:55:03 +00:00
vfb - > last_frame_render = gpuStats . numFlips ;
2013-08-07 20:32:04 +00:00
frameLastFramebufUsed = gpuStats . numFlips ;
2013-08-15 23:00:26 +00:00
vfb - > dirtyAfterDisplay = true ;
if ( ( gstate_c . skipDrawReason & SKIPDRAW_SKIPFRAME ) = = 0 )
vfb - > reallyDirtyAfterDisplay = true ;
2013-03-15 23:40:37 +00:00
}
// ugly...
if ( gstate_c . curRTWidth ! = vfb - > width | | gstate_c . curRTHeight ! = vfb - > height ) {
shaderManager_ - > DirtyUniform ( DIRTY_PROJTHROUGHMATRIX ) ;
gstate_c . curRTWidth = vfb - > width ;
gstate_c . curRTHeight = vfb - > height ;
2013-01-30 20:09:53 +00:00
}
}
2013-10-09 17:00:35 +00:00
void FramebufferManager : : SetLineWidth ( ) {
# ifndef USING_GLES2
if ( g_Config . iInternalResolution = = 0 ) {
glLineWidth ( std : : max ( 1 , ( int ) ( PSP_CoreParameter ( ) . renderWidth / 480 ) ) ) ;
glPointSize ( std : : max ( 1.0f , ( float ) ( PSP_CoreParameter ( ) . renderWidth / 480.f ) ) ) ;
} else {
glLineWidth ( g_Config . iInternalResolution ) ;
glPointSize ( ( float ) g_Config . iInternalResolution ) ;
}
# endif
}
2014-01-20 14:02:20 +00:00
void FramebufferManager : : BindFramebufferDepth ( VirtualFramebuffer * sourceframebuffer , VirtualFramebuffer * targetframebuffer ) {
if ( ! sourceframebuffer | | ! targetframebuffer - > fbo | | ! useBufferedRendering_ ) {
return ;
}
2014-01-24 09:44:24 +00:00
// If depth wasn't updated, then we're at least "two degrees" away from the data.
// This is an optimization: it probably doesn't need to be copied in this case.
if ( ! sourceframebuffer - > depthUpdated ) {
return ;
}
2014-01-20 14:02:20 +00:00
if ( MaskedEqual ( sourceframebuffer - > z_address , targetframebuffer - > z_address ) & &
sourceframebuffer - > renderWidth = = targetframebuffer - > renderWidth & &
sourceframebuffer - > renderHeight = = targetframebuffer - > renderHeight ) {
2014-01-20 01:28:11 +00:00
2014-01-20 14:02:20 +00:00
# ifndef USING_GLES2
if ( gl_extensions . FBO_ARB ) {
2014-05-25 22:10:42 +00:00
bool useNV = false ;
2014-01-20 14:02:20 +00:00
# else
2014-05-04 10:42:31 +00:00
if ( gl_extensions . GLES3 | | gl_extensions . NV_framebuffer_blit ) {
2014-05-25 22:10:42 +00:00
bool useNV = ! gl_extensions . GLES3 ;
2014-01-20 14:02:20 +00:00
# endif
# ifdef MAY_HAVE_GLES3
// Let's only do this if not clearing.
if ( ! gstate . isModeClear ( ) | | ! gstate . isClearModeDepthMask ( ) ) {
fbo_bind_for_read ( sourceframebuffer - > fbo ) ;
2014-05-10 04:26:53 +00:00
2014-05-25 22:15:21 +00:00
# if defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY)) // We only support this extension on Android, it's not even available on PC.
2014-05-25 22:10:42 +00:00
if ( useNV ) {
2014-05-08 08:40:36 +00:00
glBlitFramebufferNV ( 0 , 0 , sourceframebuffer - > renderWidth , sourceframebuffer - > renderHeight , 0 , 0 , targetframebuffer - > renderWidth , targetframebuffer - > renderHeight , GL_DEPTH_BUFFER_BIT , GL_NEAREST ) ;
2014-05-10 04:26:53 +00:00
} else
2014-05-25 22:15:21 +00:00
# endif // defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY))
2014-05-10 04:26:53 +00:00
glBlitFramebuffer ( 0 , 0 , sourceframebuffer - > renderWidth , sourceframebuffer - > renderHeight , 0 , 0 , targetframebuffer - > renderWidth , targetframebuffer - > renderHeight , GL_DEPTH_BUFFER_BIT , GL_NEAREST ) ;
2014-05-04 10:42:31 +00:00
// If we set targetframebuffer->depthUpdated here, our optimization above would be pointless.
}
2014-01-20 14:02:20 +00:00
# endif
}
}
2014-01-20 01:28:11 +00:00
}
void FramebufferManager : : BindFramebufferColor ( VirtualFramebuffer * framebuffer ) {
if ( ! framebuffer - > fbo | | ! useBufferedRendering_ ) {
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
gstate_c . skipDrawReason | = SKIPDRAW_BAD_FB_TEXTURE ;
return ;
}
2014-05-23 15:48:29 +00:00
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
// Let's just not bother with the copy in that case.
if ( currentRenderVfb_ & & MaskedEqual ( framebuffer - > fb_address , gstate . getFrameBufRawAddress ( ) ) ) {
2014-01-20 01:28:11 +00:00
# ifndef USING_GLES2
if ( gl_extensions . FBO_ARB ) {
2014-05-25 22:10:42 +00:00
bool useNV = false ;
2014-01-20 01:28:11 +00:00
# else
2014-05-25 22:10:42 +00:00
if ( gl_extensions . GLES3 | | gl_extensions . NV_framebuffer_blit ) {
bool useNV = ! gl_extensions . GLES3 ;
2014-01-20 01:28:11 +00:00
# endif
# ifdef MAY_HAVE_GLES3
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
2014-01-20 10:12:44 +00:00
FBO * renderCopy = NULL ;
std : : pair < int , int > copySize = std : : make_pair ( ( int ) framebuffer - > renderWidth , ( int ) framebuffer - > renderHeight ) ;
for ( auto it = renderCopies_ . begin ( ) , end = renderCopies_ . end ( ) ; it ! = end ; + + it ) {
if ( it - > first = = copySize ) {
renderCopy = it - > second ;
break ;
}
}
if ( ! renderCopy ) {
renderCopy = fbo_create ( framebuffer - > renderWidth , framebuffer - > renderHeight , 1 , true , framebuffer - > colorDepth ) ;
renderCopies_ [ copySize ] = renderCopy ;
2014-01-20 01:28:11 +00:00
}
2014-01-20 10:12:44 +00:00
fbo_bind_as_render_target ( renderCopy ) ;
2014-01-20 01:28:11 +00:00
glViewport ( 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight ) ;
fbo_bind_for_read ( framebuffer - > fbo ) ;
2014-05-10 03:41:24 +00:00
2014-05-25 22:15:21 +00:00
# if defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY)) // We only support this extension on Android, it's not even available on PC.
2014-05-25 22:10:42 +00:00
if ( useNV ) {
2014-05-10 04:26:53 +00:00
glBlitFramebufferNV ( 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight , 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
} else
2014-05-25 22:15:21 +00:00
# endif // defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY))
2014-05-10 04:26:53 +00:00
glBlitFramebuffer ( 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight , 0 , 0 , framebuffer - > renderWidth , framebuffer - > renderHeight , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
2014-01-20 01:28:11 +00:00
fbo_bind_as_render_target ( currentRenderVfb_ - > fbo ) ;
2014-01-20 10:12:44 +00:00
fbo_bind_color_as_texture ( renderCopy , 0 ) ;
2014-01-20 01:28:11 +00:00
# endif
} else {
fbo_bind_color_as_texture ( framebuffer - > fbo , 0 ) ;
}
} else {
fbo_bind_color_as_texture ( framebuffer - > fbo , 0 ) ;
}
2014-01-20 14:02:20 +00:00
}
2013-01-30 20:09:53 +00:00
void FramebufferManager : : CopyDisplayToOutput ( ) {
fbo_unbind ( ) ;
2013-10-22 09:19:06 +00:00
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
2013-06-09 09:11:16 +00:00
currentRenderVfb_ = 0 ;
2013-01-30 20:09:53 +00:00
2013-09-21 16:52:30 +00:00
VirtualFramebuffer * vfb = GetVFBAt ( displayFramebufPtr_ ) ;
2013-01-30 20:09:53 +00:00
if ( ! vfb ) {
2013-09-21 16:52:30 +00:00
if ( Memory : : IsValidAddress ( displayFramebufPtr_ ) ) {
2013-03-15 21:50:35 +00:00
// The game is displaying something directly from RAM. In GTA, it's decoded video.
2013-09-21 16:52:30 +00:00
// First check that it's not a known RAM copy of a VRAM framebuffer though, as in MotoGP
2014-05-09 19:17:54 +00:00
for ( auto iter = knownFramebufferRAMCopies_ . begin ( ) ; iter ! = knownFramebufferRAMCopies_ . end ( ) ; + + iter ) {
2013-09-21 16:52:30 +00:00
if ( iter - > second = = displayFramebufPtr_ ) {
vfb = GetVFBAt ( iter - > first ) ;
}
}
if ( ! vfb ) {
2013-12-04 10:46:42 +00:00
// Just a pointer to plain memory to draw. We should create a framebuffer, then draw to it.
2014-05-09 20:26:42 +00:00
DrawFramebuffer ( Memory : : GetPointer ( displayFramebufPtr_ ) , displayFormat_ , displayStride_ , true ) ;
2013-09-21 16:52:30 +00:00
return ;
}
2013-03-15 21:50:35 +00:00
} else {
2013-09-07 20:02:55 +00:00
DEBUG_LOG ( SCEGE , " Found no FBO to display! displayFBPtr = %08x " , displayFramebufPtr_ ) ;
2013-03-15 21:50:35 +00:00
// No framebuffer to display! Clear to black.
2013-08-14 19:32:43 +00:00
ClearBuffer ( ) ;
2013-09-21 16:52:30 +00:00
return ;
2013-03-15 21:50:35 +00:00
}
2013-01-30 20:09:53 +00:00
}
2013-03-03 12:00:21 +00:00
vfb - > usageFlags | = FB_USAGE_DISPLAYED_FRAMEBUFFER ;
vfb - > dirtyAfterDisplay = false ;
2013-08-15 23:00:26 +00:00
vfb - > reallyDirtyAfterDisplay = false ;
2013-03-03 12:00:21 +00:00
2013-04-12 14:09:45 +00:00
if ( prevDisplayFramebuf_ ! = displayFramebuf_ ) {
prevPrevDisplayFramebuf_ = prevDisplayFramebuf_ ;
}
if ( displayFramebuf_ ! = vfb ) {
prevDisplayFramebuf_ = displayFramebuf_ ;
}
2013-01-30 20:09:53 +00:00
displayFramebuf_ = vfb ;
2013-08-15 23:00:26 +00:00
if ( resized_ ) {
ClearBuffer ( ) ;
2013-09-26 10:41:07 +00:00
DestroyDraw2DProgram ( ) ;
2013-10-09 17:00:35 +00:00
SetLineWidth ( ) ;
2013-08-15 23:00:26 +00:00
}
2013-03-09 05:46:11 +00:00
if ( vfb - > fbo ) {
2013-09-07 20:02:55 +00:00
DEBUG_LOG ( SCEGE , " Displaying FBO %08x " , vfb - > fb_address ) ;
2013-08-14 19:32:43 +00:00
DisableState ( ) ;
2013-03-03 12:00:21 +00:00
2013-10-01 14:20:09 +00:00
GLuint colorTexture = fbo_get_color_texture ( vfb - > fbo ) ;
2013-10-22 10:17:40 +00:00
// Output coordinates
float x , y , w , h ;
CenterRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight ) ;
2013-10-09 09:01:52 +00:00
// TODO ES3: Use glInvalidateFramebuffer to discard depth/stencil data at the end of frame.
// and to discard extraFBOs_ after using them.
2013-10-22 10:17:40 +00:00
if ( ! usePostShader_ ) {
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
// These are in the output display coordinates
2014-05-08 13:28:25 +00:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight , true , 0.0f , 0.0f , 480.0f / ( float ) vfb - > width , 272.0f / ( float ) vfb - > height ) ;
2013-10-22 10:17:40 +00:00
} else if ( usePostShader_ & & extraFBOs_ . size ( ) = = 1 & & ! postShaderAtOutputResolution_ ) {
2013-10-29 19:12:19 +00:00
// An additional pass, post-processing shader to the extra FBO.
2013-09-28 20:42:13 +00:00
fbo_bind_as_render_target ( extraFBOs_ [ 0 ] ) ;
int fbo_w , fbo_h ;
fbo_get_dimensions ( extraFBOs_ [ 0 ] , & fbo_w , & fbo_h ) ;
glstate . viewport . set ( 0 , 0 , fbo_w , fbo_h ) ;
2014-05-08 13:28:25 +00:00
DrawActiveTexture ( colorTexture , 0 , 0 , fbo_w , fbo_h , fbo_w , fbo_h , true , 0.0f , 0.0f , 1.0f , 1.0f , postShaderProgram_ ) ;
2013-09-28 20:42:13 +00:00
fbo_unbind ( ) ;
2013-10-29 19:12:19 +00:00
// Use the extra FBO, with applied post-processing shader, as a texture.
2013-10-01 14:20:09 +00:00
// fbo_bind_color_as_texture(extraFBOs_[0], 0);
2013-10-22 11:40:51 +00:00
if ( extraFBOs_ . size ( ) = = 0 ) {
ERROR_LOG ( G3D , " WTF? " ) ;
return ;
}
2013-10-01 14:20:09 +00:00
colorTexture = fbo_get_color_texture ( extraFBOs_ [ 0 ] ) ;
2013-10-22 10:17:40 +00:00
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
// These are in the output display coordinates
2014-05-08 13:28:25 +00:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight , true , 0.0f , 0.0f , 480.0f / ( float ) vfb - > width , 272.0f / ( float ) vfb - > height ) ;
2013-10-22 10:17:40 +00:00
} else {
// Use post-shader, but run shader at output resolution.
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
// These are in the output display coordinates
2014-05-08 13:28:25 +00:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) PSP_CoreParameter ( ) . pixelWidth , ( float ) PSP_CoreParameter ( ) . pixelHeight , true , 0.0f , 0.0f , 480.0f / ( float ) vfb - > width , 272.0f / ( float ) vfb - > height , postShaderProgram_ ) ;
2013-09-28 20:42:13 +00:00
}
2013-03-11 21:22:22 +00:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2013-03-03 12:00:21 +00:00
}
}
2013-01-30 20:09:53 +00:00
2014-05-26 18:40:46 +00:00
inline bool FramebufferManager : : ShouldDownloadFramebuffer ( const VirtualFramebuffer * vfb ) const {
return updateVRAM_ | | ( hackForce04154000Download_ & & MaskedEqual ( vfb - > fb_address , 0x04154000 ) ) ;
}
2014-05-08 14:09:55 +00:00
void FramebufferManager : : ReadFramebufferToMemory ( VirtualFramebuffer * vfb , bool sync , int x , int y , int w , int h ) {
2014-05-09 18:48:36 +00:00
2013-08-11 18:30:17 +00:00
# ifndef USING_GLES2
2013-10-09 09:01:52 +00:00
if ( sync ) {
2013-08-11 18:30:17 +00:00
PackFramebufferAsync_ ( NULL ) ; // flush async just in case when we go for synchronous update
}
2013-10-09 09:01:52 +00:00
# endif
2013-08-11 18:30:17 +00:00
2013-10-09 09:01:52 +00:00
if ( vfb ) {
2014-05-09 18:48:36 +00:00
2013-06-28 13:48:36 +00:00
// We'll pseudo-blit framebuffers here to get a resized and flipped version of vfb.
// For now we'll keep these on the same struct as the ones that can get displayed
// (and blatantly copy work already done above while at it).
VirtualFramebuffer * nvfb = 0 ;
// We maintain a separate vector of framebuffer objects for blitting.
for ( size_t i = 0 ; i < bvfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * v = bvfbs_ [ i ] ;
if ( MaskedEqual ( v - > fb_address , vfb - > fb_address ) & & v - > format = = vfb - > format ) {
if ( v - > bufferWidth = = vfb - > bufferWidth & & v - > bufferHeight = = vfb - > bufferHeight ) {
nvfb = v ;
v - > fb_stride = vfb - > fb_stride ;
v - > width = vfb - > width ;
v - > height = vfb - > height ;
break ;
2013-06-25 12:50:35 +00:00
}
}
2013-06-28 13:48:36 +00:00
}
2013-06-25 12:50:35 +00:00
2013-06-28 13:48:36 +00:00
// Create a new fbo if none was found for the size
2013-10-09 09:01:52 +00:00
if ( ! nvfb ) {
2013-06-28 13:48:36 +00:00
nvfb = new VirtualFramebuffer ( ) ;
nvfb - > fbo = 0 ;
nvfb - > fb_address = vfb - > fb_address ;
nvfb - > fb_stride = vfb - > fb_stride ;
nvfb - > z_address = vfb - > z_address ;
nvfb - > z_stride = vfb - > z_stride ;
nvfb - > width = vfb - > width ;
nvfb - > height = vfb - > height ;
nvfb - > renderWidth = vfb - > width ;
nvfb - > renderHeight = vfb - > height ;
nvfb - > bufferWidth = vfb - > bufferWidth ;
nvfb - > bufferHeight = vfb - > bufferHeight ;
nvfb - > format = vfb - > format ;
nvfb - > usageFlags = FB_USAGE_RENDERTARGET ;
nvfb - > dirtyAfterDisplay = true ;
2013-09-22 09:03:39 +00:00
// When updating VRAM, it need to be exact format.
switch ( vfb - > format ) {
case GE_FORMAT_4444 :
nvfb - > colorDepth = FBO_4444 ;
break ;
case GE_FORMAT_5551 :
nvfb - > colorDepth = FBO_5551 ;
break ;
2013-10-10 18:11:20 +00:00
case GE_FORMAT_565 :
2013-09-22 09:03:39 +00:00
nvfb - > colorDepth = FBO_565 ;
break ;
case GE_FORMAT_8888 :
default :
nvfb - > colorDepth = FBO_8888 ;
break ;
2013-06-28 13:48:36 +00:00
}
2013-06-25 12:50:35 +00:00
2013-06-28 13:48:36 +00:00
nvfb - > fbo = fbo_create ( nvfb - > width , nvfb - > height , 1 , true , nvfb - > colorDepth ) ;
if ( ! ( nvfb - > fbo ) ) {
2013-09-07 20:02:55 +00:00
ERROR_LOG ( SCEGE , " Error creating FBO! %i x %i " , nvfb - > renderWidth , nvfb - > renderHeight ) ;
2013-08-11 18:30:17 +00:00
return ;
2013-06-25 12:50:35 +00:00
}
2013-09-01 18:55:03 +00:00
nvfb - > last_frame_render = gpuStats . numFlips ;
2013-06-28 13:48:36 +00:00
bvfbs_ . push_back ( nvfb ) ;
2013-10-10 18:11:20 +00:00
fbo_bind_as_render_target ( nvfb - > fbo ) ;
2013-08-14 19:32:43 +00:00
ClearBuffer ( ) ;
2013-07-04 14:24:37 +00:00
glEnable ( GL_DITHER ) ;
2013-06-28 13:48:36 +00:00
} else {
nvfb - > usageFlags | = FB_USAGE_RENDERTARGET ;
2014-04-13 21:45:55 +00:00
gstate_c . textureChanged | = TEXCHANGE_PARAMSONLY ;
2013-09-01 18:55:03 +00:00
nvfb - > last_frame_render = gpuStats . numFlips ;
2013-06-28 13:48:36 +00:00
nvfb - > dirtyAfterDisplay = true ;
# ifdef USING_GLES2
2013-09-11 20:59:36 +00:00
if ( nvfb - > fbo ) {
fbo_bind_as_render_target ( nvfb - > fbo ) ;
}
2013-08-11 18:30:17 +00:00
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
// to it. This broke stuff before, so now it only clears on the first use of an
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
2013-09-01 18:55:03 +00:00
if ( nvfb - > last_frame_render ! = gpuStats . numFlips ) {
2013-08-14 20:14:18 +00:00
ClearBuffer ( ) ;
2013-06-25 12:50:35 +00:00
}
2013-08-11 18:30:17 +00:00
# endif
2013-06-28 13:48:36 +00:00
}
2013-06-25 12:50:35 +00:00
2014-05-26 01:18:26 +00:00
if ( gameUsesSequentialCopies_ ) {
// Ignore the x/y/etc., read the entire thing.
x = 0 ;
y = 0 ;
w = vfb - > width ;
h = vfb - > height ;
}
if ( x = = 0 & & y = = 0 & & w = = vfb - > width & & h = = vfb - > height ) {
vfb - > memoryUpdated = true ;
} else {
const static int FREQUENT_SEQUENTIAL_COPIES = 3 ;
static int frameLastCopy = 0 ;
static u32 bufferLastCopy = 0 ;
static int copiesThisFrame = 0 ;
if ( frameLastCopy ! = gpuStats . numFlips | | bufferLastCopy ! = vfb - > fb_address ) {
frameLastCopy = gpuStats . numFlips ;
bufferLastCopy = vfb - > fb_address ;
copiesThisFrame = 0 ;
}
if ( + + copiesThisFrame > FREQUENT_SEQUENTIAL_COPIES ) {
gameUsesSequentialCopies_ = true ;
}
}
2014-05-25 22:48:28 +00:00
BlitFramebuffer_ ( nvfb , x , y , vfb , x , y , w , h , 0 , true ) ;
2013-06-25 12:50:35 +00:00
2014-05-08 08:46:19 +00:00
// PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels
// PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs
2013-07-01 17:59:44 +00:00
# ifdef USING_GLES2
2014-05-26 02:48:46 +00:00
PackFramebufferSync_ ( nvfb , x , y , w , h ) ;
2013-06-26 21:23:16 +00:00
# else
2014-01-01 23:09:01 +00:00
if ( gl_extensions . PBO_ARB & & gl_extensions . OES_texture_npot ) {
2013-10-09 09:01:52 +00:00
if ( ! sync ) {
2014-05-08 08:46:19 +00:00
PackFramebufferAsync_ ( nvfb ) ;
2013-09-29 02:30:25 +00:00
} else {
2014-05-26 02:48:46 +00:00
PackFramebufferSync_ ( nvfb , x , y , w , h ) ;
2013-09-29 02:30:25 +00:00
}
2013-08-11 18:30:17 +00:00
}
2013-06-26 21:23:16 +00:00
# endif
2014-05-09 18:48:36 +00:00
// Null out currentRenderVfb_ so it gets set again properly.
currentRenderVfb_ = NULL ;
2013-06-28 13:48:36 +00:00
}
}
2014-05-09 14:01:30 +00:00
// TODO: If dimensions are the same, we can use glCopyImageSubData.
2014-05-25 22:48:28 +00:00
void FramebufferManager : : BlitFramebuffer_ ( VirtualFramebuffer * dst , int dstX , int dstY , VirtualFramebuffer * src , int srcX , int srcY , int w , int h , int bpp , bool flip ) {
2014-05-08 13:16:33 +00:00
if ( ! dst - > fbo ) {
2013-09-11 21:29:26 +00:00
ERROR_LOG_REPORT_ONCE ( dstfbozero , SCEGE , " BlitFramebuffer_: dst->fbo == 0 " ) ;
2013-09-22 09:03:39 +00:00
fbo_unbind ( ) ;
return ;
2013-09-11 20:59:36 +00:00
}
2013-10-09 09:01:52 +00:00
2014-05-08 13:16:33 +00:00
if ( ! src - > fbo ) {
ERROR_LOG_REPORT_ONCE ( srcfbozero , SCEGE , " BlitFramebuffer_: src->fbo == 0 " ) ;
fbo_unbind ( ) ;
return ;
}
fbo_bind_as_render_target ( dst - > fbo ) ;
2014-05-09 14:01:30 +00:00
# ifndef USING_GLES2
if ( gl_extensions . FBO_ARB ) {
2014-05-25 22:10:42 +00:00
bool useNV = false ;
2014-05-09 14:01:30 +00:00
# else
if ( gl_extensions . GLES3 | | gl_extensions . NV_framebuffer_blit ) {
2014-05-25 22:10:42 +00:00
bool useNV = ! gl_extensions . GLES3 ;
2014-05-08 13:16:33 +00:00
# endif
2013-10-09 09:01:52 +00:00
2014-05-25 22:13:19 +00:00
float srcXFactor = ( float ) src - > renderWidth / ( float ) src - > bufferWidth ;
float srcYFactor = ( float ) src - > renderHeight / ( float ) src - > bufferHeight ;
int srcBpp = src - > format = = GE_FORMAT_8888 ? 4 : 2 ;
if ( srcBpp ! = bpp & & bpp ! = 0 ) {
srcXFactor = ( srcXFactor * bpp ) / srcBpp ;
}
int srcX1 = srcX * srcXFactor ;
int srcX2 = ( srcX + w ) * srcXFactor ;
int srcY2 = src - > renderHeight - ( h + srcY ) * srcYFactor ;
int srcY1 = srcY2 + h * srcYFactor ;
float dstXFactor = ( float ) dst - > renderWidth / ( float ) dst - > bufferWidth ;
float dstYFactor = ( float ) dst - > renderHeight / ( float ) dst - > bufferHeight ;
int dstBpp = dst - > format = = GE_FORMAT_8888 ? 4 : 2 ;
if ( dstBpp ! = bpp & & bpp ! = 0 ) {
dstXFactor = ( dstXFactor * bpp ) / dstBpp ;
}
int dstX1 = dstX * dstXFactor ;
int dstX2 = ( dstX + w ) * dstXFactor ;
int dstY2 = dst - > renderHeight - ( h + dstY ) * dstYFactor ;
int dstY1 = dstY2 + h * dstYFactor ;
2014-05-25 22:48:28 +00:00
if ( flip ) {
2014-05-26 00:46:42 +00:00
dstY1 = dst - > renderHeight - dstY1 ;
dstY2 = dst - > renderHeight - dstY2 ;
2014-05-25 22:48:28 +00:00
}
2014-05-09 14:01:30 +00:00
# ifdef MAY_HAVE_GLES3
2014-05-25 22:13:19 +00:00
fbo_bind_for_read ( src - > fbo ) ;
if ( ! useNV ) {
glBlitFramebuffer ( srcX1 , srcY1 , srcX2 , srcY2 , dstX1 , dstY1 , dstX2 , dstY2 , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
}
2014-05-25 22:15:21 +00:00
# if defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY)) // We only support this extension on Android, it's not even available on PC.
2014-05-25 22:13:19 +00:00
else if ( gl_extensions . NV_framebuffer_blit ) {
glBlitFramebufferNV ( srcX1 , srcY1 , srcX2 , srcY2 , dstX1 , dstY1 , dstX2 , dstY2 , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
}
2014-05-25 22:15:21 +00:00
# endif // defined(USING_GLES2) && (defined(ANDROID) || defined(BLACKBERRY))
2013-06-28 13:48:36 +00:00
2014-05-09 14:10:02 +00:00
# endif // MAY_HAVE_GLES3
2014-05-09 14:01:30 +00:00
} else {
fbo_bind_color_as_texture ( src - > fbo , 0 ) ;
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
CompileDraw2DProgram ( ) ;
glstate . viewport . set ( 0 , 0 , dst - > width , dst - > height ) ;
DisableState ( ) ;
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
// Should maybe revamp that interface.
float srcW = src - > width ;
float srcH = src - > height ;
DrawActiveTexture ( 0 , dstX , dstY , w , h , dst - > width , dst - > height , false , srcX / srcW , srcY / srcH , ( srcX + w ) / srcW , ( srcY + h ) / srcH , draw2dprogram_ ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
}
2013-10-09 09:01:52 +00:00
2013-06-28 13:48:36 +00:00
fbo_unbind ( ) ;
}
2014-05-04 07:18:01 +00:00
static inline bool UseBGRA8888 ( ) {
// TODO: Other platforms? May depend on vendor which is faster?
# ifdef _WIN32
return gl_extensions . EXT_bgra ;
# endif
return false ;
}
2013-07-06 09:09:08 +00:00
// TODO: SSE/NEON
2013-10-23 12:46:04 +00:00
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
2014-05-04 07:18:01 +00:00
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 stride , u32 height , GEBufferFormat format ) {
2013-10-09 09:01:52 +00:00
if ( format = = GE_FORMAT_8888 ) {
if ( src = = dst ) {
2013-07-02 13:08:59 +00:00
return ;
2014-05-04 07:18:01 +00:00
} else if ( UseBGRA8888 ( ) ) {
u32 numPixels = height * stride ;
ConvertBGRA8888ToRGBA8888 ( ( u32 * ) dst , ( const u32 * ) src , numPixels ) ;
2014-05-08 08:46:19 +00:00
} else {
// Here let's assume they don't intersect
2013-07-02 13:08:59 +00:00
memcpy ( dst , src , stride * height * 4 ) ;
}
2014-05-08 08:46:19 +00:00
} else {
// But here it shouldn't matter if they do intersect
2013-07-06 17:08:59 +00:00
int size = height * stride ;
2013-07-06 09:09:08 +00:00
const u32 * src32 = ( const u32 * ) src ;
2013-07-05 01:31:31 +00:00
u16 * dst16 = ( u16 * ) dst ;
switch ( format ) {
case GE_FORMAT_565 : // BGR 565
2014-05-04 07:18:01 +00:00
if ( UseBGRA8888 ( ) ) {
for ( int i = 0 ; i < size ; i + + ) {
dst16 [ i ] = BGRA8888toRGB565 ( src32 [ i ] ) ;
}
} else {
for ( int i = 0 ; i < size ; i + + ) {
dst16 [ i ] = RGBA8888toRGB565 ( src32 [ i ] ) ;
}
2013-07-05 01:31:31 +00:00
}
break ;
case GE_FORMAT_5551 : // ABGR 1555
2014-05-04 07:18:01 +00:00
if ( UseBGRA8888 ( ) ) {
ConvertBGRA8888ToRGBA5551 ( dst16 , src32 , size ) ;
} else {
ConvertRGBA8888ToRGBA5551 ( dst16 , src32 , size ) ;
2013-07-05 01:31:31 +00:00
}
break ;
case GE_FORMAT_4444 : // ABGR 4444
2014-05-04 07:18:01 +00:00
if ( UseBGRA8888 ( ) ) {
for ( int i = 0 ; i < size ; i + + ) {
dst16 [ i ] = BGRA8888toRGBA4444 ( src32 [ i ] ) ;
}
} else {
for ( int i = 0 ; i < size ; i + + ) {
dst16 [ i ] = RGBA8888toRGBA4444 ( src32 [ i ] ) ;
}
2013-07-05 01:31:31 +00:00
}
break ;
2013-07-30 06:05:59 +00:00
case GE_FORMAT_8888 :
2014-05-04 07:18:01 +00:00
case GE_FORMAT_INVALID :
2013-07-30 06:05:59 +00:00
// Not possible.
break ;
2013-06-25 12:50:35 +00:00
}
2013-06-28 13:48:36 +00:00
}
}
2013-07-05 22:40:01 +00:00
# ifndef USING_GLES2
2014-05-08 08:43:46 +00:00
// TODO: Make more generic.
static void LogReadPixelsError ( GLenum error ) {
switch ( error ) {
2014-05-08 12:30:40 +00:00
case GL_NO_ERROR :
2014-05-08 08:43:46 +00:00
break ;
case GL_INVALID_ENUM :
ERROR_LOG ( SCEGE , " glReadPixels: GL_INVALID_ENUM " ) ;
break ;
case GL_INVALID_VALUE :
ERROR_LOG ( SCEGE , " glReadPixels: GL_INVALID_VALUE " ) ;
break ;
case GL_INVALID_OPERATION :
ERROR_LOG ( SCEGE , " glReadPixels: GL_INVALID_OPERATION " ) ;
break ;
case GL_INVALID_FRAMEBUFFER_OPERATION :
ERROR_LOG ( SCEGE , " glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION " ) ;
break ;
2014-05-08 12:30:40 +00:00
case GL_OUT_OF_MEMORY :
ERROR_LOG ( SCEGE , " glReadPixels: GL_OUT_OF_MEMORY " ) ;
break ;
case GL_STACK_UNDERFLOW :
ERROR_LOG ( SCEGE , " glReadPixels: GL_STACK_UNDERFLOW " ) ;
break ;
case GL_STACK_OVERFLOW :
ERROR_LOG ( SCEGE , " glReadPixels: GL_STACK_OVERFLOW " ) ;
2014-05-08 08:43:46 +00:00
break ;
}
}
2013-08-11 18:30:17 +00:00
void FramebufferManager : : PackFramebufferAsync_ ( VirtualFramebuffer * vfb ) {
const int MAX_PBO = 2 ;
2013-06-28 13:48:36 +00:00
GLubyte * packed = 0 ;
2013-07-02 13:08:59 +00:00
bool unbind = false ;
2013-08-11 18:30:17 +00:00
u8 nextPBO = ( currentPBO_ + 1 ) % MAX_PBO ;
2013-08-14 19:20:06 +00:00
bool useCPU = g_Config . iRenderingMode = = FB_READFBOMEMORY_CPU ;
2013-08-11 18:30:17 +00:00
2013-07-04 14:24:37 +00:00
// We'll prepare two PBOs to switch between readying and reading
2013-10-09 09:01:52 +00:00
if ( ! pixelBufObj_ ) {
2013-08-11 18:30:17 +00:00
GLuint pbos [ MAX_PBO ] ;
glGenBuffers ( MAX_PBO , pbos ) ;
pixelBufObj_ = new AsyncPBO [ MAX_PBO ] ;
2013-10-09 09:01:52 +00:00
for ( int i = 0 ; i < MAX_PBO ; i + + ) {
2013-08-11 18:30:17 +00:00
pixelBufObj_ [ i ] . handle = pbos [ i ] ;
pixelBufObj_ [ i ] . maxSize = 0 ;
pixelBufObj_ [ i ] . reading = false ;
}
}
// Receive previously requested data from a PBO
2014-05-04 07:18:01 +00:00
AsyncPBO & pbo = pixelBufObj_ [ nextPBO ] ;
if ( pbo . reading ) {
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo . handle ) ;
2013-08-11 18:30:17 +00:00
packed = ( GLubyte * ) glMapBuffer ( GL_PIXEL_PACK_BUFFER , GL_READ_ONLY ) ;
2013-07-02 13:08:59 +00:00
2013-10-09 09:01:52 +00:00
if ( packed ) {
2013-10-19 21:04:46 +00:00
DEBUG_LOG ( SCEGE , " Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u " ,
2014-05-04 07:18:01 +00:00
pbo . size , packed , pbo . fb_address , pbo . stride , nextPBO ) ;
2013-08-11 18:30:17 +00:00
2014-05-04 07:18:01 +00:00
if ( useCPU | | ( UseBGRA8888 ( ) & & pbo . format = = GE_FORMAT_8888 ) ) {
u8 * dst = Memory : : GetPointer ( pbo . fb_address ) ;
ConvertFromRGBA8888 ( dst , packed , pbo . stride , pbo . height , pbo . format ) ;
2013-10-09 09:01:52 +00:00
} else {
2013-09-22 09:03:39 +00:00
// We don't need to convert, GPU already did (or should have)
2014-05-04 07:18:01 +00:00
Memory : : Memcpy ( pbo . fb_address , packed , pbo . size ) ;
2013-08-11 18:30:17 +00:00
}
2013-07-02 13:08:59 +00:00
2014-05-04 07:18:01 +00:00
pbo . reading = false ;
2013-08-11 18:30:17 +00:00
}
2013-07-02 13:08:59 +00:00
2013-08-11 18:30:17 +00:00
glUnmapBuffer ( GL_PIXEL_PACK_BUFFER ) ;
unbind = true ;
2013-07-02 13:08:59 +00:00
}
2013-06-28 13:48:36 +00:00
// Order packing/readback of the framebuffer
2013-10-09 09:01:52 +00:00
if ( vfb ) {
2013-06-25 12:50:35 +00:00
int pixelType , pixelSize , pixelFormat , align ;
2013-10-15 22:47:57 +00:00
bool reverseOrder = ( gl_extensions . gpuVendor = = GPU_VENDOR_NVIDIA ) | | ( gl_extensions . gpuVendor = = GPU_VENDOR_AMD ) ;
2013-06-25 12:50:35 +00:00
switch ( vfb - > format ) {
2013-07-04 14:24:37 +00:00
// GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC)
// GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value)
// We want R G B A, so we use *_REV for 16-bit formats and GL_UNSIGNED_BYTE for 32-bit
case GE_FORMAT_4444 : // 16 bit RGBA
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4 ) ;
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
align = 8 ;
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_5551 : // 16 bit RGBA
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1 ) ;
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
align = 8 ;
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_565 : // 16 bit RGB
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5 ) ;
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGB ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
align = 8 ;
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_8888 : // 32 bit RGBA
2013-06-28 13:48:36 +00:00
default :
pixelType = GL_UNSIGNED_BYTE ;
2014-05-04 07:18:01 +00:00
pixelFormat = UseBGRA8888 ( ) ? GL_BGRA_EXT : GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 4 ;
align = 4 ;
break ;
2013-06-25 12:50:35 +00:00
}
2014-05-04 03:20:40 +00:00
// If using the CPU, we need 4 bytes per pixel always.
u32 bufSize = vfb - > fb_stride * vfb - > height * ( useCPU ? 4 : pixelSize ) ;
2013-09-19 17:28:14 +00:00
u32 fb_address = ( 0x04000000 ) | vfb - > fb_address ;
2013-06-28 13:48:36 +00:00
2013-08-11 18:30:17 +00:00
if ( vfb - > fbo ) {
fbo_bind_for_read ( vfb - > fbo ) ;
} else {
2013-12-22 08:03:39 +00:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferAsync_: vfb->fbo == 0 " ) ;
2013-08-11 18:30:17 +00:00
fbo_unbind ( ) ;
2013-10-09 09:01:52 +00:00
if ( gl_extensions . FBO_ARB ) {
2013-08-11 18:30:17 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2013-06-25 12:50:35 +00:00
}
2013-08-11 18:30:17 +00:00
return ;
}
2013-10-09 09:01:52 +00:00
if ( glCheckFramebufferStatus ( GL_READ_FRAMEBUFFER ) ! = GL_FRAMEBUFFER_COMPLETE ) {
2013-09-07 20:02:55 +00:00
ERROR_LOG ( SCEGE , " Incomplete source framebuffer, aborting read " ) ;
2013-08-11 18:30:17 +00:00
fbo_unbind ( ) ;
2013-10-09 09:01:52 +00:00
if ( gl_extensions . FBO_ARB ) {
2013-08-11 18:30:17 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
}
return ;
2013-06-25 12:50:35 +00:00
}
2013-06-28 13:48:36 +00:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pixelBufObj_ [ currentPBO_ ] . handle ) ;
2013-10-09 09:01:52 +00:00
if ( pixelBufObj_ [ currentPBO_ ] . maxSize < bufSize ) {
2013-07-04 14:24:37 +00:00
// We reserve a buffer big enough to fit all those pixels
2014-05-04 03:20:40 +00:00
glBufferData ( GL_PIXEL_PACK_BUFFER , bufSize , NULL , GL_DYNAMIC_READ ) ;
2013-06-28 13:48:36 +00:00
pixelBufObj_ [ currentPBO_ ] . maxSize = bufSize ;
2013-06-25 12:50:35 +00:00
}
2013-10-09 09:01:52 +00:00
if ( useCPU ) {
2013-07-04 14:24:37 +00:00
// If converting pixel formats on the CPU we'll always request RGBA8888
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2014-05-04 07:18:01 +00:00
glReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , UseBGRA8888 ( ) ? GL_BGRA_EXT : GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2013-06-28 13:48:36 +00:00
} else {
2013-07-04 14:24:37 +00:00
// Otherwise we'll directly request the format we need and let the GPU sort it out
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , align ) ;
glReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , pixelFormat , pixelType , 0 ) ;
}
2013-07-02 13:08:59 +00:00
2014-05-08 12:30:40 +00:00
// LogReadPixelsError(glGetError());
2013-08-11 18:30:17 +00:00
2013-07-04 14:24:37 +00:00
fbo_unbind ( ) ;
2013-10-09 09:01:52 +00:00
if ( gl_extensions . FBO_ARB ) {
2013-07-04 14:24:37 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
}
2013-07-02 13:08:59 +00:00
unbind = true ;
2013-06-28 13:48:36 +00:00
2013-08-11 18:30:17 +00:00
pixelBufObj_ [ currentPBO_ ] . fb_address = fb_address ;
pixelBufObj_ [ currentPBO_ ] . size = bufSize ;
pixelBufObj_ [ currentPBO_ ] . stride = vfb - > fb_stride ;
pixelBufObj_ [ currentPBO_ ] . height = vfb - > height ;
pixelBufObj_ [ currentPBO_ ] . format = vfb - > format ;
pixelBufObj_ [ currentPBO_ ] . reading = true ;
2013-06-28 13:48:36 +00:00
}
currentPBO_ = nextPBO ;
2013-10-09 09:01:52 +00:00
if ( unbind ) {
2013-07-02 13:08:59 +00:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
2013-06-28 13:48:36 +00:00
}
}
2013-07-05 22:40:01 +00:00
# endif
2014-05-26 02:48:46 +00:00
void FramebufferManager : : PackFramebufferSync_ ( VirtualFramebuffer * vfb , int x , int y , int w , int h ) {
2013-09-22 09:03:39 +00:00
if ( vfb - > fbo ) {
2013-07-02 13:08:59 +00:00
fbo_bind_for_read ( vfb - > fbo ) ;
} else {
2013-09-22 09:03:39 +00:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferSync_: vfb->fbo == 0 " ) ;
2013-07-02 13:08:59 +00:00
fbo_unbind ( ) ;
2013-12-22 08:03:39 +00:00
if ( gl_extensions . FBO_ARB ) {
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
}
2013-07-02 13:08:59 +00:00
return ;
}
2013-07-04 14:24:37 +00:00
// Pixel size always 4 here because we always request RGBA8888
size_t bufSize = vfb - > fb_stride * vfb - > height * 4 ;
2013-08-11 18:30:17 +00:00
u32 fb_address = ( 0x04000000 ) | vfb - > fb_address ;
2013-06-28 13:48:36 +00:00
2013-07-02 13:08:59 +00:00
GLubyte * packed = 0 ;
2014-05-09 19:01:17 +00:00
bool convert = vfb - > format ! = GE_FORMAT_8888 | | UseBGRA8888 ( ) ;
2014-05-09 19:26:46 +00:00
if ( ! convert ) {
2013-07-02 13:08:59 +00:00
packed = ( GLubyte * ) Memory : : GetPointer ( fb_address ) ;
2013-07-04 14:24:37 +00:00
} else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
2014-05-26 02:48:46 +00:00
u32 neededSize = ( u32 ) bufSize * sizeof ( GLubyte ) ;
if ( ! convBuf_ | | convBufSize_ < neededSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ neededSize ] ;
convBufSize_ = neededSize ;
}
packed = convBuf_ ;
2013-06-28 13:48:36 +00:00
}
2013-10-09 09:01:52 +00:00
if ( packed ) {
2013-09-07 20:02:55 +00:00
DEBUG_LOG ( SCEGE , " Reading framebuffer to mem, bufSize = %u, packed = %p, fb_address = %08x " ,
2013-07-06 17:08:59 +00:00
( u32 ) bufSize , packed , fb_address ) ;
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2014-05-04 15:43:30 +00:00
GLenum glfmt = GL_RGBA ;
# if defined(MAY_HAVE_GLES3)
2014-05-09 19:01:17 +00:00
if ( UseBGRA8888 ( ) ) {
2014-05-04 15:43:30 +00:00
glfmt = GL_BGRA_EXT ;
2014-05-09 19:01:17 +00:00
}
2014-05-04 15:43:30 +00:00
# endif
2014-05-26 02:48:46 +00:00
int byteOffset = y * vfb - > fb_stride * 4 ;
glReadPixels ( 0 , y , vfb - > fb_stride , h , glfmt , GL_UNSIGNED_BYTE , packed + byteOffset ) ;
2014-05-08 08:43:46 +00:00
// LogReadPixelsError(glGetError());
2013-06-28 13:48:36 +00:00
2014-05-09 19:01:17 +00:00
if ( convert ) {
2014-05-26 02:48:46 +00:00
ConvertFromRGBA8888 ( Memory : : GetPointer ( fb_address + byteOffset ) , packed + byteOffset , vfb - > fb_stride , h , vfb - > format ) ;
2013-06-28 13:48:36 +00:00
}
}
fbo_unbind ( ) ;
2013-06-25 12:50:35 +00:00
}
2013-03-03 12:00:21 +00:00
void FramebufferManager : : EndFrame ( ) {
2013-01-30 20:09:53 +00:00
if ( resized_ ) {
DestroyAllFBOs ( ) ;
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
2014-01-26 13:59:40 +00:00
int zoom = g_Config . iInternalResolution ;
2014-01-27 06:46:09 +00:00
if ( zoom = = 0 ) // auto mode
zoom = ( PSP_CoreParameter ( ) . pixelWidth + 479 ) / 480 ;
PSP_CoreParameter ( ) . renderWidth = 480 * zoom ;
PSP_CoreParameter ( ) . renderHeight = 272 * zoom ;
2013-01-30 20:09:53 +00:00
resized_ = false ;
}
2013-07-02 13:10:20 +00:00
# ifndef USING_GLES2
2014-05-08 12:26:17 +00:00
// We flush to memory last requested framebuffer, if any.
// Only do this in the read-framebuffer modes.
if ( g_Config . iRenderingMode = = FB_READFBOMEMORY_CPU | | g_Config . iRenderingMode = = FB_READFBOMEMORY_GPU )
PackFramebufferAsync_ ( NULL ) ;
2013-07-02 13:10:20 +00:00
# endif
2013-01-30 20:09:53 +00:00
}
2013-06-11 09:28:41 +00:00
void FramebufferManager : : DeviceLost ( ) {
DestroyAllFBOs ( ) ;
2013-09-26 10:41:07 +00:00
DestroyDraw2DProgram ( ) ;
2013-06-11 09:28:41 +00:00
resized_ = false ;
}
2013-01-30 20:09:53 +00:00
void FramebufferManager : : BeginFrame ( ) {
DecimateFBOs ( ) ;
currentRenderVfb_ = 0 ;
2013-08-14 19:20:06 +00:00
useBufferedRendering_ = g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ;
2013-01-30 20:09:53 +00:00
}
2013-07-30 06:05:59 +00:00
void FramebufferManager : : SetDisplayFramebuffer ( u32 framebuf , u32 stride , GEBufferFormat format ) {
2013-09-21 16:52:30 +00:00
displayFramebufPtr_ = framebuf ;
displayStride_ = stride ;
displayFormat_ = format ;
2013-01-30 20:09:53 +00:00
}
2013-03-03 12:00:21 +00:00
std : : vector < FramebufferInfo > FramebufferManager : : GetFramebufferList ( ) {
2013-02-17 00:06:06 +00:00
std : : vector < FramebufferInfo > list ;
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-02-17 00:06:06 +00:00
FramebufferInfo info ;
info . fb_address = vfb - > fb_address ;
info . z_address = vfb - > z_address ;
info . format = vfb - > format ;
info . width = vfb - > width ;
info . height = vfb - > height ;
info . fbo = vfb - > fbo ;
list . push_back ( info ) ;
}
return list ;
}
2013-04-12 14:00:59 +00:00
void FramebufferManager : : DecimateFBOs ( ) {
2013-03-11 21:22:22 +00:00
fbo_unbind ( ) ;
2013-06-09 09:11:16 +00:00
currentRenderVfb_ = 0 ;
2013-09-22 09:03:39 +00:00
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-09-01 18:55:03 +00:00
int age = frameLastFramebufUsed - std : : max ( vfb - > last_frame_render , vfb - > last_frame_used ) ;
2013-07-02 13:10:20 +00:00
2014-05-26 18:40:46 +00:00
if ( ShouldDownloadFramebuffer ( vfb ) & & age = = 0 & & ! vfb - > memoryUpdated ) {
2014-05-08 14:09:55 +00:00
# ifdef USING_GLES2
bool sync = true ;
# else
bool sync = false ;
# endif
ReadFramebufferToMemory ( vfb , sync , 0 , 0 , vfb - > width , vfb - > height ) ;
}
2013-09-29 02:30:25 +00:00
if ( vfb = = displayFramebuf_ | | vfb = = prevDisplayFramebuf_ | | vfb = = prevPrevDisplayFramebuf_ ) {
2013-01-30 20:09:53 +00:00
continue ;
}
2013-07-01 17:35:38 +00:00
2013-07-02 13:10:20 +00:00
if ( age > FBO_OLD_AGE ) {
2013-09-07 20:02:55 +00:00
INFO_LOG ( SCEGE , " Decimating FBO for %08x (%i x %i x %i), age %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format , age )
2013-06-23 15:16:22 +00:00
DestroyFramebuf ( vfb ) ;
2013-06-23 15:51:35 +00:00
vfbs_ . erase ( vfbs_ . begin ( ) + i - - ) ;
2013-01-30 20:09:53 +00:00
}
}
2013-06-28 13:48:36 +00:00
// Do the same for ReadFramebuffersToMemory's VFBs
for ( size_t i = 0 ; i < bvfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = bvfbs_ [ i ] ;
2013-09-01 18:55:03 +00:00
int age = frameLastFramebufUsed - vfb - > last_frame_render ;
2013-06-28 13:48:36 +00:00
if ( age > FBO_OLD_AGE ) {
2013-09-07 20:02:55 +00:00
INFO_LOG ( SCEGE , " Decimating FBO for %08x (%i x %i x %i), age %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format , age )
2013-06-28 13:48:36 +00:00
DestroyFramebuf ( vfb ) ;
bvfbs_ . erase ( bvfbs_ . begin ( ) + i - - ) ;
}
}
2013-01-30 20:09:53 +00:00
}
void FramebufferManager : : DestroyAllFBOs ( ) {
2013-03-11 21:22:22 +00:00
fbo_unbind ( ) ;
2013-06-09 09:11:16 +00:00
currentRenderVfb_ = 0 ;
2013-06-10 20:07:48 +00:00
displayFramebuf_ = 0 ;
prevDisplayFramebuf_ = 0 ;
prevPrevDisplayFramebuf_ = 0 ;
2013-06-09 09:11:16 +00:00
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-09-07 20:02:55 +00:00
INFO_LOG ( SCEGE , " Destroying FBO for %08x : %i x %i x %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format ) ;
2013-06-23 15:16:22 +00:00
DestroyFramebuf ( vfb ) ;
2013-01-30 20:09:53 +00:00
}
vfbs_ . clear ( ) ;
}
2013-01-31 11:14:57 +00:00
2013-10-26 07:36:37 +00:00
void FramebufferManager : : UpdateFromMemory ( u32 addr , int size , bool safe ) {
2013-06-08 11:37:40 +00:00
addr & = ~ 0x40000000 ;
// TODO: Could go through all FBOs, but probably not important?
// TODO: Could also check for inner changes, but video is most important.
2013-10-26 07:36:37 +00:00
if ( addr = = DisplayFramebufAddr ( ) | | addr = = PrevDisplayFramebufAddr ( ) | | safe ) {
2013-06-08 11:37:40 +00:00
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
if ( ! Memory : : IsValidAddress ( displayFramebufPtr_ ) )
return ;
2013-06-10 07:10:39 +00:00
bool needUnbind = false ;
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-06-08 11:37:40 +00:00
if ( MaskedEqual ( vfb - > fb_address , addr ) ) {
2014-04-14 04:11:17 +00:00
fbo_unbind ( ) ;
currentRenderVfb_ = 0 ;
2013-07-18 18:00:32 +00:00
vfb - > dirtyAfterDisplay = true ;
2013-08-16 07:27:49 +00:00
vfb - > reallyDirtyAfterDisplay = true ;
2013-06-08 11:37:40 +00:00
// TODO: This without the fbo_unbind() above would be better than destroying the FBO.
// However, it doesn't seem to work for Star Ocean, at least
2013-09-11 20:59:36 +00:00
if ( useBufferedRendering_ & & vfb - > fbo ) {
2013-10-26 07:32:03 +00:00
DisableState ( ) ;
2013-06-10 07:10:39 +00:00
fbo_bind_as_render_target ( vfb - > fbo ) ;
2014-04-13 04:11:47 +00:00
int w = vfb - > bufferWidth ;
int h = vfb - > bufferHeight ;
// Often, the framebuffer size is incorrect. But here we have the size. Bit of a hack.
if ( vfb - > fb_stride = = 512 & & ( size = = 512 * 272 * 4 | | size = = 512 * 272 * 2 ) ) {
// Looks like a standard 480x272 sized framebuffer/video/etc.
w = 480 ;
h = 272 ;
}
// Scale by the render resolution factor.
w = ( w * vfb - > renderWidth ) / vfb - > bufferWidth ;
h = ( h * vfb - > renderHeight ) / vfb - > bufferHeight ;
glstate . viewport . set ( 0 , vfb - > renderHeight - h , w , h ) ;
2013-06-10 07:10:39 +00:00
needUnbind = true ;
2014-05-09 20:26:42 +00:00
DrawPixels ( vfb , 0 , 0 , Memory : : GetPointer ( addr | 0x04000000 ) , vfb - > format , vfb - > fb_stride , vfb - > width , vfb - > height ) ;
2013-06-10 07:10:39 +00:00
} else {
2013-09-07 20:02:55 +00:00
INFO_LOG ( SCEGE , " Invalidating FBO for %08x (%i x %i x %i) " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format )
2013-06-23 15:16:22 +00:00
DestroyFramebuf ( vfb ) ;
2013-06-23 15:51:35 +00:00
vfbs_ . erase ( vfbs_ . begin ( ) + i - - ) ;
2013-06-08 11:37:40 +00:00
}
}
}
2013-06-10 07:10:39 +00:00
if ( needUnbind )
fbo_unbind ( ) ;
2013-06-08 11:37:40 +00:00
}
}
2014-05-26 03:35:27 +00:00
bool FramebufferManager : : NotifyFramebufferCopy ( u32 src , u32 dst , int size ) {
if ( ! ( g_Config . iRenderingMode = = FB_BUFFERED_MODE ) ) {
return false ;
}
2014-05-09 19:17:54 +00:00
// MotoGP workaround
2014-05-26 00:03:32 +00:00
if ( Memory : : IsVRAMAddress ( src ) & & Memory : : IsRAMAddress ( dst ) ) {
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; i + + ) {
int bpp = vfbs_ [ i ] - > format = = GE_FORMAT_8888 ? 4 : 2 ;
2014-05-26 00:12:58 +00:00
int fsize = FramebufferByteSize ( vfbs_ [ i ] ) ;
2014-05-26 00:03:32 +00:00
if ( MaskedEqual ( vfbs_ [ i ] - > fb_address , src ) & & size = = fsize ) {
// A framebuffer matched!
knownFramebufferRAMCopies_ . insert ( std : : pair < u32 , u32 > ( src , dst ) ) ;
}
2014-05-09 19:17:54 +00:00
}
}
VirtualFramebuffer * dstBuffer = 0 ;
VirtualFramebuffer * srcBuffer = 0 ;
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
if ( MaskedEqual ( vfb - > fb_address , dst ) ) {
dstBuffer = vfb ;
}
if ( MaskedEqual ( vfb - > fb_address , src ) ) {
srcBuffer = vfb ;
}
}
// TODO: Do ReadFramebufferToMemory etc where applicable.
// This will slow down MotoGP but make the hack above unnecessary.
if ( dstBuffer & & srcBuffer ) {
if ( srcBuffer = = dstBuffer ) {
WARN_LOG_REPORT_ONCE ( dstsrccpy , G3D , " Intra-buffer memcpy (not supported) %08x -> %08x " , src , dst ) ;
} else {
2014-05-26 00:10:06 +00:00
WARN_LOG_REPORT_ONCE ( dstnotsrccpy , G3D , " Inter-buffer memcpy (not supported) %08x -> %08x " , src , dst ) ;
2014-05-09 19:17:54 +00:00
// Just do the blit!
2014-05-25 08:18:14 +00:00
// if (g_Config.bBlockTransferGPU) {
2014-05-25 22:13:19 +00:00
// BlitFramebuffer_(dstBuffer, 0, 0, srcBuffer, 0, 0, srcBuffer->width, srcBuffer->height, 0);
2014-05-25 08:18:14 +00:00
// }
2014-05-09 19:17:54 +00:00
}
2014-05-26 03:35:27 +00:00
Memory : : Memcpy ( dst , Memory : : GetPointer ( src ) , size ) ;
return true ;
2014-05-09 19:17:54 +00:00
} else if ( dstBuffer ) {
2014-05-26 05:00:51 +00:00
WARN_LOG_REPORT_ONCE ( btucpy , G3D , " Memcpy fbo upload %08x -> %08x " , src , dst ) ;
if ( g_Config . bBlockTransferGPU ) {
const u8 * srcBase = Memory : : GetPointerUnchecked ( src ) ;
fbo_bind_as_render_target ( dstBuffer - > fbo ) ;
2014-05-26 08:21:30 +00:00
glViewport ( 0 , 0 , dstBuffer - > renderWidth , dstBuffer - > renderHeight ) ;
2014-05-26 05:00:51 +00:00
// TODO: Validate x/y/w/h based on size and offset?
DrawPixels ( dstBuffer , 0 , 0 , srcBase , dstBuffer - > format , dstBuffer - > fb_stride , dstBuffer - > width , dstBuffer - > height ) ;
dstBuffer - > dirtyAfterDisplay = true ;
if ( ( gstate_c . skipDrawReason & SKIPDRAW_SKIPFRAME ) = = 0 )
dstBuffer - > reallyDirtyAfterDisplay = true ;
if ( currentRenderVfb_ ) {
fbo_bind_as_render_target ( currentRenderVfb_ - > fbo ) ;
} else {
fbo_unbind ( ) ;
}
2014-05-26 08:21:30 +00:00
glstate . viewport . restore ( ) ;
2014-05-26 05:35:14 +00:00
gstate_c . textureChanged = TEXCHANGE_PARAMSONLY ;
2014-05-26 05:00:51 +00:00
// This is a memcpy, let's still copy just in case.
return false ;
}
return false ;
} else if ( srcBuffer ) {
2014-05-26 03:35:27 +00:00
WARN_LOG_REPORT_ONCE ( btdcpy , G3D , " Memcpy fbo download %08x -> %08x " , src , dst ) ;
if ( g_Config . bBlockTransferGPU ) {
// TODO: Validate x/y/w/h based on size and offset?
ReadFramebufferToMemory ( srcBuffer , true , 0 , 0 , srcBuffer - > width , srcBuffer - > height ) ;
}
return false ;
} else {
return false ;
2014-05-09 19:17:54 +00:00
}
}
2014-05-26 00:12:58 +00:00
u32 FramebufferManager : : FramebufferByteSize ( const VirtualFramebuffer * vfb ) const {
return vfb - > fb_stride * vfb - > height * ( vfb - > format = = GE_FORMAT_8888 ? 4 : 2 ) ;
}
2014-05-25 23:00:39 +00:00
void FramebufferManager : : FindTransferFramebuffers ( VirtualFramebuffer * & dstBuffer , VirtualFramebuffer * & srcBuffer , u32 dstBasePtr , int dstStride , int & dstX , int & dstY , u32 srcBasePtr , int srcStride , int & srcX , int & srcY , int bpp ) const {
2014-05-26 05:57:07 +00:00
u32 dstYOffset = - 1 ;
u32 srcYOffset = - 1 ;
2014-05-08 14:25:44 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2014-05-25 22:13:19 +00:00
const u32 vfb_address = 0x04000000 | vfb - > fb_address ;
2014-05-26 00:12:58 +00:00
const u32 vfb_size = FramebufferByteSize ( vfb ) ;
2014-05-25 22:13:19 +00:00
if ( vfb_address < = dstBasePtr & & dstBasePtr < vfb_address + vfb_size ) {
2014-05-26 05:57:07 +00:00
const u32 yOffset = ( dstBasePtr - vfb_address ) / ( dstStride * bpp ) ;
if ( yOffset < dstYOffset ) {
dstYOffset = yOffset ;
dstBuffer = vfb ;
}
2014-01-20 09:47:20 +00:00
}
2014-05-25 22:13:19 +00:00
if ( vfb_address < = srcBasePtr & & srcBasePtr < vfb_address + vfb_size ) {
2014-05-26 05:57:07 +00:00
const u32 yOffset = ( srcBasePtr - vfb_address ) / ( srcStride * bpp ) ;
if ( yOffset < srcYOffset ) {
srcYOffset = yOffset ;
srcBuffer = vfb ;
}
2014-01-20 09:47:20 +00:00
}
}
2014-05-26 00:45:31 +00:00
2014-05-26 05:57:07 +00:00
if ( dstYOffset ! = ( u32 ) - 1 ) {
dstY + = dstYOffset ;
}
if ( srcYOffset > = ( u32 ) - 1 ) {
srcY + = srcYOffset ;
}
2014-05-25 23:00:39 +00:00
}
bool FramebufferManager : : NotifyBlockTransferBefore ( u32 dstBasePtr , int dstStride , int dstX , int dstY , u32 srcBasePtr , int srcStride , int srcX , int srcY , int width , int height , int bpp ) {
if ( ! ( g_Config . iRenderingMode = = FB_BUFFERED_MODE ) ) {
return false ;
}
2014-05-26 00:01:28 +00:00
// Skip checking if there's no framebuffers in that area.
if ( ! MayIntersectFramebuffer ( srcBasePtr ) & & ! MayIntersectFramebuffer ( dstBasePtr ) ) {
return false ;
}
2014-05-25 23:00:39 +00:00
VirtualFramebuffer * dstBuffer = 0 ;
VirtualFramebuffer * srcBuffer = 0 ;
FindTransferFramebuffers ( dstBuffer , srcBuffer , dstBasePtr , dstStride , dstX , dstY , srcBasePtr , srcStride , srcX , srcY , bpp ) ;
2014-01-20 09:47:20 +00:00
2014-05-08 14:25:44 +00:00
if ( dstBuffer & & srcBuffer ) {
if ( srcBuffer = = dstBuffer ) {
WARN_LOG_REPORT_ONCE ( dstsrc , G3D , " Intra-buffer block transfer (not supported) %08x -> %08x " , srcBasePtr , dstBasePtr ) ;
} else {
WARN_LOG_ONCE ( dstnotsrc , G3D , " Inter-buffer block transfer %08x -> %08x " , srcBasePtr , dstBasePtr ) ;
// Just do the blit!
2014-05-25 08:18:14 +00:00
if ( g_Config . bBlockTransferGPU ) {
2014-05-25 22:13:19 +00:00
BlitFramebuffer_ ( dstBuffer , dstX , dstY , srcBuffer , srcX , srcY , width , height , bpp ) ;
2014-05-25 08:50:28 +00:00
return true ; // No need to actually do the memory copy behind, probably.
2014-05-25 08:18:14 +00:00
}
2014-05-08 14:25:44 +00:00
}
2014-05-25 08:50:28 +00:00
return false ;
2014-05-08 14:25:44 +00:00
} else if ( dstBuffer ) {
2014-05-25 23:00:39 +00:00
// Here we should just draw the pixels into the buffer. Copy first.
2014-05-08 14:25:44 +00:00
return false ;
2014-05-25 23:00:39 +00:00
} else if ( srcBuffer ) {
2014-05-08 14:25:44 +00:00
WARN_LOG_ONCE ( btd , G3D , " Block transfer download %08x -> %08x " , srcBasePtr , dstBasePtr ) ;
2014-05-26 00:24:09 +00:00
if ( g_Config . bBlockTransferGPU & & ( srcBuffer = = currentRenderVfb_ | | ! srcBuffer - > memoryUpdated ) ) {
2014-05-25 08:18:14 +00:00
ReadFramebufferToMemory ( srcBuffer , true , srcX , srcY , width , height ) ;
}
2014-05-08 14:25:44 +00:00
return false ; // Let the bit copy happen
} else {
return false ;
}
2014-01-20 09:47:20 +00:00
}
2014-05-25 23:00:39 +00:00
void FramebufferManager : : NotifyBlockTransferAfter ( u32 dstBasePtr , int dstStride , int dstX , int dstY , u32 srcBasePtr , int srcStride , int srcX , int srcY , int width , int height , int bpp ) {
if ( ! ( g_Config . iRenderingMode = = FB_BUFFERED_MODE ) ) {
return ;
}
// A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to
// the backbuffer. Detect this and have the framebuffermanager draw the pixels.
u32 backBuffer = PrevDisplayFramebufAddr ( ) ;
u32 displayBuffer = DisplayFramebufAddr ( ) ;
2014-05-26 00:01:28 +00:00
// TODO: Is this not handled by upload? Should we check !dstBuffer to avoid a double copy?
2014-05-25 23:00:39 +00:00
if ( ( ( backBuffer ! = 0 & & dstBasePtr = = backBuffer ) | |
( displayBuffer ! = 0 & & dstBasePtr = = displayBuffer ) ) & &
dstStride = = 512 & & height = = 272 ) {
// TODO: Use displayFormat_ instead of GE_FORMAT_8888?
DrawFramebuffer ( Memory : : GetPointerUnchecked ( dstBasePtr ) , GE_FORMAT_8888 , 512 , false ) ;
}
2014-05-26 00:01:28 +00:00
if ( MayIntersectFramebuffer ( srcBasePtr ) | | MayIntersectFramebuffer ( dstBasePtr ) ) {
VirtualFramebuffer * dstBuffer = 0 ;
VirtualFramebuffer * srcBuffer = 0 ;
FindTransferFramebuffers ( dstBuffer , srcBuffer , dstBasePtr , dstStride , dstX , dstY , srcBasePtr , srcStride , srcX , srcY , bpp ) ;
2014-05-25 23:00:39 +00:00
2014-05-26 00:01:28 +00:00
if ( dstBuffer & & ! srcBuffer ) {
2014-05-26 00:10:06 +00:00
WARN_LOG_REPORT_ONCE ( btu , G3D , " Block transfer upload %08x -> %08x " , srcBasePtr , dstBasePtr ) ;
2014-05-26 00:01:28 +00:00
if ( g_Config . bBlockTransferGPU ) {
2014-05-26 05:00:51 +00:00
const u8 * srcBase = Memory : : GetPointerUnchecked ( srcBasePtr ) + ( srcX + srcY * srcStride ) * bpp ;
2014-05-26 03:23:11 +00:00
fbo_bind_as_render_target ( dstBuffer - > fbo ) ;
2014-05-26 07:52:06 +00:00
int dstBpp = dstBuffer - > format = = GE_FORMAT_8888 ? 4 : 2 ;
float dstXFactor = ( float ) bpp / dstBpp ;
2014-05-26 08:21:30 +00:00
glViewport ( 0 , 0 , dstBuffer - > renderWidth , dstBuffer - > renderHeight ) ;
2014-05-26 07:52:06 +00:00
DrawPixels ( dstBuffer , dstX * dstXFactor , dstY , srcBase , dstBuffer - > format , srcStride * dstXFactor , width * dstXFactor , height ) ;
2014-05-26 05:00:51 +00:00
dstBuffer - > dirtyAfterDisplay = true ;
if ( ( gstate_c . skipDrawReason & SKIPDRAW_SKIPFRAME ) = = 0 )
dstBuffer - > reallyDirtyAfterDisplay = true ;
2014-05-26 03:23:11 +00:00
if ( currentRenderVfb_ ) {
fbo_bind_as_render_target ( currentRenderVfb_ - > fbo ) ;
} else {
fbo_unbind ( ) ;
}
2014-05-26 08:21:30 +00:00
glstate . viewport . restore ( ) ;
2014-05-26 05:35:14 +00:00
gstate_c . textureChanged = TEXCHANGE_PARAMSONLY ;
2014-05-26 00:01:28 +00:00
}
2014-05-25 23:00:39 +00:00
}
}
}
2013-01-31 11:14:57 +00:00
void FramebufferManager : : Resized ( ) {
resized_ = true ;
}
2013-09-23 02:03:31 +00:00
2013-09-28 09:14:27 +00:00
bool FramebufferManager : : GetCurrentFramebuffer ( GPUDebugBuffer & buffer ) {
2013-10-06 22:51:31 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2013-09-23 02:03:31 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2013-10-06 00:19:17 +00:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , gstate . FrameBufFormat ( ) ) ;
2013-09-23 02:03:31 +00:00
return true ;
}
2013-11-11 08:42:33 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GE_FORMAT_8888 , true , true ) ;
2013-09-29 05:03:36 +00:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2013-10-06 00:19:17 +00:00
# ifndef USING_GLES2
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
# endif
2013-09-23 02:03:31 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2013-09-23 03:03:47 +00:00
glReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_RGBA , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2013-09-23 02:03:31 +00:00
return true ;
}
2013-09-28 09:14:27 +00:00
bool FramebufferManager : : GetCurrentDepthbuffer ( GPUDebugBuffer & buffer ) {
2013-10-06 22:51:31 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2013-09-28 09:14:27 +00:00
2013-10-06 22:51:31 +00:00
u32 z_address = gstate . getDepthBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int z_stride = gstate . DepthBufStride ( ) ;
2013-09-28 09:14:27 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2013-09-28 09:38:05 +00:00
// TODO: Is the value 16-bit? It seems to be.
2013-10-06 00:19:17 +00:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( z_address | 0x04000000 ) , z_stride , 512 , GPU_DBG_FORMAT_16BIT ) ;
2013-09-28 09:14:27 +00:00
return true ;
}
# ifndef USING_GLES2
2013-10-06 00:19:17 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_16BIT , true ) ;
2013-09-29 05:03:36 +00:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2013-10-06 00:19:17 +00:00
glReadBuffer ( GL_DEPTH_ATTACHMENT ) ;
2013-09-28 09:14:27 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2013-10-06 00:19:17 +00:00
glReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_DEPTH_COMPONENT , GL_UNSIGNED_SHORT , buffer . GetData ( ) ) ;
2013-09-28 09:14:27 +00:00
return true ;
# else
return false ;
# endif
}
bool FramebufferManager : : GetCurrentStencilbuffer ( GPUDebugBuffer & buffer ) {
2013-10-06 22:51:31 +00:00
u32 fb_address = gstate . getFrameBufRawAddress ( ) ;
2013-12-15 17:48:44 +00:00
int fb_stride = gstate . FrameBufStride ( ) ;
2013-09-28 09:14:27 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2013-10-06 00:19:17 +00:00
// TODO: Actually get the stencil.
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , GPU_DBG_FORMAT_8888 ) ;
2013-09-28 09:14:27 +00:00
return true ;
}
# ifndef USING_GLES2
2013-10-07 00:11:42 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_8BIT , true ) ;
2013-09-29 05:03:36 +00:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2013-10-06 00:19:17 +00:00
glReadBuffer ( GL_STENCIL_ATTACHMENT ) ;
2013-09-28 09:14:27 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 2 ) ;
2013-10-06 00:19:17 +00:00
glReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_STENCIL_INDEX , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2013-09-28 09:14:27 +00:00
return true ;
# else
return false ;
# endif
}