2014-05-27 01:14:29 -07:00
// Copyright (c) 2012- PPSSPP Project.
2012-11-01 16:19:01 +01:00
// 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 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01: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 23:44:35 +01:00
# include <set>
# include <algorithm>
2015-07-12 00:44:11 +02:00
# include "profiler/profiler.h"
2012-11-01 16:19:01 +01:00
# include "gfx_es2/glsl_program.h"
2013-01-30 21:09:53 +01:00
2013-12-02 17:24:20 +01:00
# include "base/timeutil.h"
2012-11-01 16:19:01 +01:00
# include "math/lin/matrix4x4.h"
2015-04-08 00:16:22 +02:00
# include "Common/ColorConv.h"
2013-01-30 21:09:53 +01:00
# include "Core/Host.h"
# include "Core/MemMap.h"
# include "Core/Config.h"
# include "Core/System.h"
2013-09-11 22:21:15 +02:00
# include "Core/Reporting.h"
2013-12-02 17:24:20 +01:00
# include "Core/HLE/sceDisplay.h"
2013-01-30 21:09:53 +01:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2012-11-01 16:19:01 +01:00
2013-10-12 02:05:55 +02:00
# include "GPU/Common/PostShader.h"
2014-05-04 00:18:01 -07:00
# include "GPU/Common/TextureDecoder.h"
2015-01-23 10:40:49 +01:00
# include "GPU/Common/FramebufferCommon.h"
2014-06-13 08:35:12 -07:00
# include "GPU/Debugger/Stepping.h"
2015-09-06 13:45:17 +02:00
# include "GPU/GLES/GLStateCache.h"
2015-09-06 13:05:18 +02:00
# include "GPU/GLES/FBO.h"
2013-01-30 21:09:53 +01:00
# include "GPU/GLES/Framebuffer.h"
2013-02-01 00:18:23 +01:00
# include "GPU/GLES/TextureCache.h"
2014-06-11 00:28:28 -07:00
# include "GPU/GLES/TransformPipeline.h"
2013-03-16 00:40:37 +01:00
# include "GPU/GLES/ShaderManager.h"
2012-11-01 16:19:01 +01:00
2013-10-13 12:05:50 +02:00
# include "UI/OnScreenDisplay.h"
2015-12-06 09:08:38 -08:00
// #define DEBUG_READ_PIXELS 1
2013-06-30 03:35:28 +08:00
extern int g_iNumVideos ;
2013-06-26 23:23:16 +02:00
2013-01-30 21:09:53 +01:00
static const char tex_fs [ ] =
2013-10-21 00:36:23 +02:00
# ifdef USING_GLES2
2012-11-01 16:19:01 +01:00
" precision mediump float; \n "
2013-10-21 00:36:23 +02:00
# endif
2012-11-01 16:19:01 +01:00
" uniform sampler2D sampler0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2014-06-17 23:10:38 -07:00
" gl_FragColor = texture2D(sampler0, v_texcoord0); \n "
2012-11-01 16:19:01 +01:00
" } \n " ;
2013-01-30 21:09:53 +01:00
static const char basic_vs [ ] =
2012-11-01 16:19:01 +01:00
" attribute vec4 a_position; \n "
" attribute vec2 a_texcoord0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2012-12-21 11:24:38 +01:00
" v_texcoord0 = a_texcoord0; \n "
2013-10-30 14:37:07 +01:00
" gl_Position = a_position; \n "
2012-11-01 16:19:01 +01:00
" } \n " ;
2013-10-30 22:44:01 +01: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 " ;
2014-09-13 16:37:59 -07:00
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 dstStride , u32 srcStride , u32 width , u32 height , GEBufferFormat format ) ;
2013-06-28 14:48:36 +01:00
2015-11-03 22:37:19 -08:00
void FramebufferManager : : ClearBuffer ( bool keepState ) {
if ( keepState ) {
glstate . scissorTest . force ( false ) ;
glstate . depthWrite . force ( GL_TRUE ) ;
glstate . colorMask . force ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
glstate . stencilFunc . force ( GL_ALWAYS , 0 , 0 ) ;
glstate . stencilMask . force ( 0xFF ) ;
} else {
glstate . scissorTest . disable ( ) ;
glstate . depthWrite . set ( GL_TRUE ) ;
glstate . colorMask . set ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
glstate . stencilFunc . set ( GL_ALWAYS , 0 , 0 ) ;
glstate . stencilMask . set ( 0xFF ) ;
}
2014-06-17 23:57:20 -07:00
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
glClearStencil ( 0 ) ;
2014-01-01 23:40:35 +01:00
# ifdef USING_GLES2
2014-06-17 23:56:33 -07:00
glClearDepthf ( 0.0f ) ;
2014-01-01 23:40:35 +01:00
# else
2014-06-17 23:56:33 -07:00
glClearDepth ( 0.0 ) ;
2014-01-01 23:40:35 +01:00
# endif
2013-08-15 03:20:06 +08:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
2015-11-03 22:37:19 -08:00
if ( keepState ) {
glstate . scissorTest . restore ( ) ;
glstate . depthWrite . restore ( ) ;
glstate . colorMask . restore ( ) ;
glstate . stencilFunc . restore ( ) ;
glstate . stencilMask . restore ( ) ;
}
2013-08-15 03:20:06 +08:00
}
2014-06-17 23:55:09 -07:00
void FramebufferManager : : ClearDepthBuffer ( ) {
glstate . scissorTest . disable ( ) ;
glstate . depthWrite . set ( GL_TRUE ) ;
# ifdef USING_GLES2
2014-06-17 23:56:33 -07:00
glClearDepthf ( 0.0f ) ;
2014-06-17 23:55:09 -07:00
# else
2014-06-17 23:56:33 -07:00
glClearDepth ( 0.0 ) ;
2014-06-17 23:55:09 -07:00
# endif
glClear ( GL_DEPTH_BUFFER_BIT ) ;
}
2014-05-31 18:24:35 -07:00
void FramebufferManager : : DisableState ( ) {
2013-08-15 03:32:43 +08:00
glstate . blend . disable ( ) ;
glstate . cullFace . disable ( ) ;
glstate . depthTest . disable ( ) ;
glstate . scissorTest . disable ( ) ;
glstate . stencilTest . disable ( ) ;
2013-08-23 17:32:10 +08:00
# if !defined(USING_GLES2)
glstate . colorLogicOp . disable ( ) ;
# endif
2013-12-04 14:02:40 +01:00
glstate . colorMask . set ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2014-08-03 21:42:21 -07:00
glstate . stencilMask . set ( 0xFF ) ;
2013-08-15 03:32:43 +08:00
}
2013-09-28 22:42:13 +02:00
void FramebufferManager : : SetNumExtraFBOs ( int num ) {
2013-09-29 20:28:19 -07:00
for ( size_t i = 0 ; i < extraFBOs_ . size ( ) ; i + + ) {
2013-09-28 22:42:13 +02:00
fbo_destroy ( extraFBOs_ [ i ] ) ;
}
extraFBOs_ . clear ( ) ;
for ( int i = 0 ; i < num ; i + + ) {
// No depth/stencil for post processing
2015-09-19 16:19:03 +02:00
FBO * fbo = fbo_create ( renderWidth_ , renderHeight_ , 1 , false , FBO_8888 ) ;
2013-09-28 22:42:13 +02:00
extraFBOs_ . push_back ( fbo ) ;
2014-06-23 00:17:35 -07:00
2014-09-13 18:46:59 -07:00
// The new FBO is still bound after creation, but let's bind it anyway.
fbo_bind_as_render_target ( fbo ) ;
2014-06-23 00:17:35 -07:00
ClearBuffer ( ) ;
2013-09-28 22:42:13 +02:00
}
2014-06-23 00:17:35 -07:00
currentRenderVfb_ = 0 ;
fbo_unbind ( ) ;
2013-09-28 22:42:13 +02:00
}
2013-07-16 22:50:53 +02:00
void FramebufferManager : : CompileDraw2DProgram ( ) {
2013-09-26 12:41:07 +02:00
if ( ! draw2dprogram_ ) {
2013-10-13 12:05:50 +02: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 12:41:07 +02:00
2013-10-30 22:44:01 +01: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 16:08:36 +02:00
SetNumExtraFBOs ( 0 ) ;
2013-10-12 02:05:55 +02:00
const ShaderInfo * shaderInfo = 0 ;
if ( g_Config . sPostShaderName ! = " Off " ) {
shaderInfo = GetPostShaderInfo ( g_Config . sPostShaderName ) ;
}
if ( shaderInfo ) {
2013-10-22 12:17:40 +02:00
postShaderAtOutputResolution_ = shaderInfo - > outputResolution ;
2013-10-13 12:05:50 +02:00
postShaderProgram_ = glsl_create ( shaderInfo - > vertexShaderFile . c_str ( ) , shaderInfo - > fragmentShaderFile . c_str ( ) , & errorString ) ;
2013-10-12 02:05:55 +02:00
if ( ! postShaderProgram_ ) {
2013-10-13 12:05:50 +02: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 01:25:33 -08: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 09:34:54 -07:00
for ( size_t i = 0 ; i < errorString . size ( ) ; i + + ) {
2013-10-13 12:05:50 +02:00
if ( errorString [ i ] = = ' \n ' ) {
2014-02-16 01:25:33 -08:00
firstLine = errorString . substr ( start , i - start ) ;
if ( blacklistedLines . find ( firstLine ) = = blacklistedLines . end ( ) ) {
break ;
}
start = i + 1 ;
firstLine . clear ( ) ;
2013-10-13 12:05:50 +02:00
}
}
2014-02-16 01:25:33 -08: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 02:05:55 +02:00
usePostShader_ = false ;
2013-10-09 16:08:36 +02:00
} else {
2013-10-12 02:05:55 +02:00
glsl_bind ( postShaderProgram_ ) ;
glUniform1i ( postShaderProgram_ - > sampler0 , 0 ) ;
2013-10-09 16:08:36 +02:00
SetNumExtraFBOs ( 1 ) ;
2015-10-31 16:02:25 +01:00
deltaLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_texelDelta " ) ;
pixelDeltaLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_pixelDelta " ) ;
2013-12-02 17:24:20 +01:00
timeLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_time " ) ;
2013-10-12 02:05:55 +02:00
usePostShader_ = true ;
2013-10-09 16:08:36 +02:00
}
2013-09-28 22:42:13 +02:00
} else {
2015-08-05 12:13:14 +02:00
postShaderProgram_ = nullptr ;
2013-10-12 02:05:55 +02:00
usePostShader_ = false ;
2013-09-26 12:41:07 +02:00
}
2013-07-16 22:50:53 +02:00
glsl_unbind ( ) ;
}
}
2015-11-02 22:22:48 +01:00
void FramebufferManager : : UpdatePostShaderUniforms ( int bufferWidth , int bufferHeight , int renderWidth , int renderHeight ) {
2015-10-31 16:02:25 +01:00
float u_delta = 1.0f / renderWidth ;
float v_delta = 1.0f / renderHeight ;
float u_pixel_delta = u_delta ;
float v_pixel_delta = v_delta ;
if ( postShaderAtOutputResolution_ ) {
float x , y , w , h ;
2015-11-03 00:24:19 +01:00
CenterDisplayOutputRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ , ROTATION_LOCKED_HORIZONTAL ) ;
2015-11-02 22:22:48 +01:00
u_pixel_delta = ( 1.0f / w ) * ( 480.0f / bufferWidth ) ;
v_pixel_delta = ( 1.0f / h ) * ( 272.0f / bufferHeight ) ;
2015-10-31 16:02:25 +01:00
}
if ( deltaLoc_ ! = - 1 )
glUniform2f ( deltaLoc_ , u_delta , v_delta ) ;
if ( pixelDeltaLoc_ ! = - 1 )
glUniform2f ( pixelDeltaLoc_ , u_pixel_delta , v_pixel_delta ) ;
if ( 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-09-26 12:41:07 +02:00
void FramebufferManager : : DestroyDraw2DProgram ( ) {
if ( draw2dprogram_ ) {
glsl_destroy ( draw2dprogram_ ) ;
2015-08-05 12:13:14 +02:00
draw2dprogram_ = nullptr ;
2013-10-09 16:08:36 +02:00
}
2013-12-03 00:09:48 +01:00
if ( plainColorProgram_ ) {
glsl_destroy ( plainColorProgram_ ) ;
2015-08-05 12:13:14 +02:00
plainColorProgram_ = nullptr ;
2013-12-03 00:09:48 +01:00
}
2013-10-12 02:05:55 +02:00
if ( postShaderProgram_ ) {
glsl_destroy ( postShaderProgram_ ) ;
2015-08-05 12:13:14 +02:00
postShaderProgram_ = nullptr ;
2013-09-26 12:41:07 +02:00
}
}
2013-01-30 21:09:53 +01:00
FramebufferManager : : FramebufferManager ( ) :
2013-06-05 23:03:23 +02:00
drawPixelsTex_ ( 0 ) ,
2013-07-29 23:05:59 -07:00
drawPixelsTexFormat_ ( GE_FORMAT_INVALID ) ,
2015-08-05 12:13:14 +02:00
convBuf_ ( nullptr ) ,
draw2dprogram_ ( nullptr ) ,
postShaderProgram_ ( nullptr ) ,
stencilUploadProgram_ ( nullptr ) ,
2013-10-31 00:07:33 +01:00
plainColorLoc_ ( - 1 ) ,
2013-12-02 17:24:20 +01:00
timeLoc_ ( - 1 ) ,
2015-10-31 16:02:25 +01:00
deltaLoc_ ( - 1 ) ,
pixelDeltaLoc_ ( - 1 ) ,
2015-08-05 12:13:14 +02:00
textureCache_ ( nullptr ) ,
shaderManager_ ( nullptr ) ,
2014-05-25 17:01:28 -07:00
resized_ ( false ) ,
2015-09-05 23:39:10 +02:00
gameUsesSequentialCopies_ ( false ) ,
2015-08-05 12:13:14 +02:00
pixelBufObj_ ( nullptr ) ,
2013-06-28 14:48:36 +01:00
currentPBO_ ( 0 )
2013-01-30 21:09:53 +01:00
{
2014-06-15 15:19:49 -07:00
}
2013-01-14 19:26:10 +01:00
2014-06-15 15:19:49 -07:00
void FramebufferManager : : Init ( ) {
2014-09-13 16:47:23 -07:00
FramebufferManagerCommon : : Init ( ) ;
2015-10-14 20:07:06 +02:00
// Workaround for upscaling shaders where we force x1 resolution without saving it
resized_ = true ;
2014-06-15 15:19:49 -07:00
CompileDraw2DProgram ( ) ;
2013-10-09 19:00:35 +02:00
SetLineWidth ( ) ;
2012-11-01 16:19:01 +01:00
}
2012-12-01 02:15:46 +01:00
FramebufferManager : : ~ FramebufferManager ( ) {
2013-06-05 23:03:23 +02:00
if ( drawPixelsTex_ )
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
2015-01-24 20:36:43 -08:00
DestroyDraw2DProgram ( ) ;
2014-05-31 18:24:35 -07:00
if ( stencilUploadProgram_ ) {
glsl_destroy ( stencilUploadProgram_ ) ;
}
2013-09-28 22:42:13 +02:00
SetNumExtraFBOs ( 0 ) ;
2013-06-28 14:48:36 +01:00
2014-05-31 22:41:41 -07:00
for ( auto it = tempFBOs_ . begin ( ) , end = tempFBOs_ . end ( ) ; it ! = end ; + + it ) {
fbo_destroy ( it - > second . fbo ) ;
2014-01-20 02:12:44 -08:00
}
2014-01-19 18:27:52 -08:00
2013-06-28 14:48:36 +01:00
delete [ ] pixelBufObj_ ;
2014-05-25 16:28:13 -07:00
delete [ ] convBuf_ ;
2012-11-01 16:19:01 +01:00
}
2014-05-09 22:26:42 +02: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 23:03:23 +02:00
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
drawPixelsTex_ = 0 ;
}
if ( ! drawPixelsTex_ ) {
2014-06-07 15:25:35 -07:00
drawPixelsTex_ = textureCache_ - > AllocTextureName ( ) ;
2014-05-09 22:26:42 +02:00
drawPixelsTexW_ = width ;
drawPixelsTexH_ = height ;
2013-06-05 23:03:23 +02:00
// Initialize backbuffer texture for DrawPixels
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
2014-06-15 19:42:24 -07:00
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 ) ;
2013-06-05 23:03:23 +02:00
2014-05-09 23:11:04 +02:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , width , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2014-05-09 22:26:42 +02:00
drawPixelsTexFormat_ = srcPixelFormat ;
2014-05-09 23:11:04 +02:00
} else {
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
2013-06-05 23:03:23 +02:00
}
2013-06-05 23:26:51 +02:00
// TODO: We can just change the texture format and flip some bits around instead of this.
2014-05-09 23:11:04 +02:00
// Could share code with the texture cache perhaps.
2013-10-20 19:55:01 -07:00
bool useConvBuf = false ;
2014-05-09 23:11:04 +02:00
if ( srcPixelFormat ! = GE_FORMAT_8888 | | srcStride ! = width ) {
2013-10-20 19:55:01 -07:00
useConvBuf = true ;
2014-05-25 16:28:13 -07:00
u32 neededSize = width * height * 4 ;
if ( ! convBuf_ | | convBufSize_ < neededSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ neededSize ] ;
convBufSize_ = neededSize ;
2013-06-05 23:26:51 +02:00
}
2014-05-09 23:11:04 +02:00
for ( int y = 0 ; y < height ; y + + ) {
2014-05-09 22:26:42 +02:00
switch ( srcPixelFormat ) {
2013-07-29 22:58:37 -07:00
case GE_FORMAT_565 :
2012-11-01 16:19:01 +01:00
{
2014-05-09 22:26:42 +02:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 16:28:13 -07:00
u8 * dst = convBuf_ + 4 * width * y ;
2015-04-08 20:21:48 +02:00
ConvertRGBA565ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 16:19:01 +01:00
}
2013-06-05 23:26:51 +02:00
break ;
2012-11-01 16:19:01 +01:00
2013-07-29 22:58:37 -07:00
case GE_FORMAT_5551 :
2012-11-01 16:19:01 +01:00
{
2014-05-09 22:26:42 +02:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 16:28:13 -07:00
u8 * dst = convBuf_ + 4 * width * y ;
2015-04-08 20:21:48 +02:00
ConvertRGBA5551ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 16:19:01 +01:00
}
2013-06-05 23:26:51 +02:00
break ;
2012-11-01 16:19:01 +01:00
2013-07-29 22:58:37 -07:00
case GE_FORMAT_4444 :
2012-11-01 16:19:01 +01:00
{
2014-05-09 22:26:42 +02:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2014-05-25 16:28:13 -07:00
u8 * dst = convBuf_ + 4 * width * y ;
2015-04-08 20:21:48 +02:00
ConvertRGBA4444ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 16:19:01 +01:00
}
2013-06-05 23:26:51 +02:00
break ;
2012-11-01 16:19:01 +01:00
2013-07-29 22:58:37 -07:00
case GE_FORMAT_8888 :
2012-11-01 16:19:01 +01:00
{
2014-05-09 22:26:42 +02:00
const u8 * src = srcPixels + srcStride * 4 * y ;
2014-05-25 16:28:13 -07:00
u8 * dst = convBuf_ + 4 * width * y ;
2014-05-09 23:11:04 +02:00
memcpy ( dst , src , 4 * width ) ;
2012-11-01 16:19:01 +01:00
}
2013-06-05 23:26:51 +02:00
break ;
2013-08-12 23:40:22 -07:00
case GE_FORMAT_INVALID :
_dbg_assert_msg_ ( G3D , false , " Invalid pixelFormat passed to DrawPixels(). " ) ;
break ;
2012-11-01 16:19:01 +01:00
}
}
}
2014-05-25 16:28:13 -07:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , width , height , GL_RGBA , GL_UNSIGNED_BYTE , useConvBuf ? convBuf_ : srcPixels ) ;
2014-05-09 22:26:42 +02:00
}
void FramebufferManager : : DrawPixels ( VirtualFramebuffer * vfb , int dstX , int dstY , const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , int width , int height ) {
2015-11-01 15:34:53 +01:00
float v0 = 0.0f , v1 = 1.0f ;
if ( useBufferedRendering_ & & vfb & & vfb - > fbo ) {
2014-09-07 21:53:22 -07:00
fbo_bind_as_render_target ( vfb - > fbo ) ;
2015-10-31 20:06:50 +01:00
glViewport ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight ) ;
} else {
2015-11-01 15:34:53 +01:00
// We are drawing to the back buffer so need to flip.
v0 = 1.0f ;
v1 = 0.0f ;
2015-10-31 20:06:50 +01:00
float x , y , w , h ;
2015-11-03 00:24:19 +01:00
CenterDisplayOutputRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ , ROTATION_LOCKED_HORIZONTAL ) ;
2015-10-31 20:06:50 +01:00
glViewport ( x , y , w , h ) ;
2014-09-07 21:53:22 -07:00
}
2015-11-01 15:34:53 +01:00
2014-05-09 22:26:42 +02:00
MakePixelTexture ( srcPixels , srcPixelFormat , srcStride , width , height ) ;
2014-05-09 23:11:04 +02:00
DisableState ( ) ;
2015-11-01 15:34:53 +01:00
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , dstX , dstY , width , height , vfb - > bufferWidth , vfb - > bufferHeight , 0.0f , v0 , 1.0f , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2014-09-13 14:57:45 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
2014-05-09 22:26:42 +02:00
}
2015-11-01 15:34:53 +01:00
void FramebufferManager : : DrawFramebufferToOutput ( const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , bool applyPostShader ) {
2015-11-13 01:14:56 +01:00
2014-05-09 22:26:42 +02:00
MakePixelTexture ( srcPixels , srcPixelFormat , srcStride , 512 , 272 ) ;
2015-03-15 19:25:34 -07:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , g_Config . iTexFiltering = = TEX_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR ) ;
2013-02-12 22:09:14 +01:00
2013-12-04 14:02:40 +01:00
DisableState ( ) ;
2015-01-24 20:01:09 +01:00
struct CardboardSettings cardboardSettings ;
GetCardboardSettings ( & cardboardSettings ) ;
2015-01-22 12:48:25 +01:00
2013-12-04 11:46:42 +01: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 23:11:04 +02:00
float x , y , w , h ;
2015-05-12 22:44:02 +02:00
int uvRotation = ( g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ) ? g_Config . iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL ;
2015-11-03 00:24:19 +01:00
CenterDisplayOutputRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ , uvRotation ) ;
2015-10-31 16:02:25 +01:00
if ( applyPostShader ) {
2016-01-02 14:22:20 -08:00
// Make sure we've compiled the shader.
if ( ! postShaderProgram_ ) {
CompileDraw2DProgram ( ) ;
}
// Might've changed if the shader was just changed to Off.
if ( usePostShader_ ) {
glsl_bind ( postShaderProgram_ ) ;
UpdatePostShaderUniforms ( 480 , 272 , renderWidth_ , renderHeight_ ) ;
}
2015-10-31 16:02:25 +01:00
}
2015-11-01 15:34:53 +01:00
float u0 = 0.0f , u1 = 480.0f / 512.0f ;
float v0 = 0.0f , v1 = 1.0f ;
// We are drawing directly to the back buffer.
std : : swap ( v0 , v1 ) ;
2015-01-24 20:01:09 +01:00
if ( cardboardSettings . enabled ) {
// Left Eye Image
glstate . viewport . set ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
if ( applyPostShader & & usePostShader_ & & useBufferedRendering_ ) {
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , postShaderProgram_ , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
} else {
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
}
// Right Eye Image
glstate . viewport . set ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
if ( applyPostShader & & usePostShader_ & & useBufferedRendering_ ) {
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , postShaderProgram_ , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
} else {
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
}
} else {
// Fullscreen Image
2015-09-19 16:19:03 +02:00
glstate . viewport . set ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2015-01-24 20:01:09 +01:00
if ( applyPostShader & & usePostShader_ & & useBufferedRendering_ ) {
2015-11-01 15:34:53 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , postShaderProgram_ , uvRotation ) ;
2015-01-24 20:01:09 +01:00
} else {
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , uvRotation ) ;
2015-01-24 20:01:09 +01:00
}
}
2012-11-19 23:29:14 +01:00
}
2013-10-30 22:44:01 +01: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 ,
} ;
2014-05-31 18:43:35 -07:00
shaderManager_ - > DirtyLastShader ( ) ;
2013-10-30 22:44:01 +01:00
glsl_bind ( program ) ;
glUniform4fv ( plainColorLoc_ , 1 , col ) ;
glEnableVertexAttribArray ( program - > a_position ) ;
2015-12-13 22:48:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
transformDraw_ - > BindBuffer ( pos , sizeof ( pos ) ) ;
transformDraw_ - > BindElementBuffer ( indices , sizeof ( indices ) ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , 0 ) ;
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , 0 ) ;
} else {
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , pos ) ;
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , indices ) ;
}
2013-10-30 22:44:01 +01:00
glDisableVertexAttribArray ( program - > a_position ) ;
glsl_unbind ( ) ;
}
2014-05-08 15:16:33 +02:00
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
2015-10-31 23:59:23 +01:00
void FramebufferManager : : DrawActiveTexture ( GLuint texture , float x , float y , float w , float h , float destW , float destH , float u0 , float v0 , float u1 , float v1 , GLSLProgram * program , int uvRotation ) {
2015-05-12 21:01:15 +02:00
float texCoords [ 8 ] = {
u0 , v0 ,
u1 , v0 ,
u1 , v1 ,
2015-11-01 22:23:37 +01:00
u0 , v1 ,
2015-05-12 21:01:15 +02:00
} ;
2015-12-13 22:48:21 -08:00
static const GLubyte indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
2013-12-06 13:01:34 +01:00
2015-05-12 21:01:15 +02:00
if ( uvRotation ! = ROTATION_LOCKED_HORIZONTAL ) {
float temp [ 8 ] ;
int rotation = 0 ;
switch ( uvRotation ) {
case ROTATION_LOCKED_HORIZONTAL180 : rotation = 4 ; break ;
case ROTATION_LOCKED_VERTICAL : rotation = 2 ; break ;
case ROTATION_LOCKED_VERTICAL180 : rotation = 6 ; break ;
}
for ( int i = 0 ; i < 8 ; i + + ) {
temp [ i ] = texCoords [ ( i + rotation ) & 7 ] ;
}
memcpy ( texCoords , temp , sizeof ( temp ) ) ;
}
2013-10-22 12:17:40 +02:00
if ( texture ) {
2015-09-05 22:11:28 +02:00
// Previously had NVDrawTexture fallback here but wasn't worth it.
2013-10-22 12:17:40 +02:00
glBindTexture ( GL_TEXTURE_2D , texture ) ;
}
2013-10-30 14:37:07 +01:00
float pos [ 12 ] = {
x , y , 0 ,
x + w , y , 0 ,
x + w , y + h , 0 ,
x , y + h , 0
} ;
2014-05-08 15:28:25 +02:00
float invDestW = 1.0f / ( destW * 0.5f ) ;
float invDestH = 1.0f / ( destH * 0.5f ) ;
2013-10-30 14:37:07 +01:00
for ( int i = 0 ; i < 4 ; i + + ) {
2014-05-08 15:28:25 +02:00
pos [ i * 3 ] = pos [ i * 3 ] * invDestW - 1.0f ;
2015-11-01 15:34:53 +01:00
pos [ i * 3 + 1 ] = pos [ i * 3 + 1 ] * invDestH - 1.0f ;
2013-10-30 14:37:07 +01:00
}
2013-09-28 22:42:13 +02:00
if ( ! program ) {
2013-10-22 15:54:53 +02:00
if ( ! draw2dprogram_ ) {
CompileDraw2DProgram ( ) ;
}
2013-09-26 12:41:07 +02:00
program = draw2dprogram_ ;
2013-06-28 14:48:36 +01:00
}
2015-10-14 18:32:13 +02:00
// Upscaling postshaders doesn't look well with linear
if ( postShaderIsUpscalingFilter_ ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
} else {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , g_Config . iBufFilter = = SCALE_NEAREST ? GL_NEAREST : GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , g_Config . iBufFilter = = SCALE_NEAREST ? GL_NEAREST : GL_LINEAR ) ;
}
2013-12-16 16:04:08 +01:00
2015-10-31 16:02:25 +01:00
if ( program ! = postShaderProgram_ ) {
shaderManager_ - > DirtyLastShader ( ) ; // dirty lastShader_
glsl_bind ( program ) ;
2013-12-02 17:24:20 +01:00
}
2015-10-31 16:02:25 +01:00
2013-06-28 14:48:36 +01:00
glEnableVertexAttribArray ( program - > a_position ) ;
glEnableVertexAttribArray ( program - > a_texcoord0 ) ;
2015-12-13 22:48:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
transformDraw_ - > BindBuffer ( pos , sizeof ( pos ) , texCoords , sizeof ( texCoords ) ) ;
transformDraw_ - > BindElementBuffer ( indices , sizeof ( indices ) ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , 0 ) ;
glVertexAttribPointer ( program - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , ( void * ) sizeof ( pos ) ) ;
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , 0 ) ;
} else {
glstate . arrayBuffer . unbind ( ) ;
glstate . elementArrayBuffer . unbind ( ) ;
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , pos ) ;
glVertexAttribPointer ( program - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , texCoords ) ;
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_BYTE , indices ) ;
}
2013-06-28 14:48:36 +01:00
glDisableVertexAttribArray ( program - > a_position ) ;
glDisableVertexAttribArray ( program - > a_texcoord0 ) ;
2013-10-10 20:11:20 +02:00
2012-11-01 16:19:01 +01:00
glsl_unbind ( ) ;
}
2013-01-30 21:09:53 +01:00
2013-06-23 08:16:22 -07:00
void FramebufferManager : : DestroyFramebuf ( VirtualFramebuffer * v ) {
2013-07-31 23:19:15 -07:00
textureCache_ - > NotifyFramebuffer ( v - > fb_address , v , NOTIFY_FB_DESTROYED ) ;
2013-06-23 08:16:22 -07:00
if ( v - > fbo ) {
fbo_destroy ( v - > fbo ) ;
v - > fbo = 0 ;
}
2013-06-23 17:05:59 +02: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 08:16:22 -07:00
delete v ;
2013-06-23 17:05:59 +02:00
}
2014-03-30 00:11:01 +01:00
void FramebufferManager : : RebindFramebuffer ( ) {
2014-06-07 00:52:58 -07:00
if ( currentRenderVfb_ & & currentRenderVfb_ - > fbo ) {
2014-06-04 00:58:44 -07:00
fbo_bind_as_render_target ( currentRenderVfb_ - > fbo ) ;
2014-08-27 23:08:17 -07:00
} else {
fbo_unbind ( ) ;
2014-06-04 00:58:44 -07:00
}
2015-02-03 21:51:39 +01:00
if ( g_Config . iRenderingMode = = FB_NON_BUFFERED_MODE )
glstate . viewport . restore ( ) ;
2014-03-30 00:11:01 +01:00
}
2014-06-06 20:11:14 -07:00
void FramebufferManager : : ResizeFramebufFBO ( VirtualFramebuffer * vfb , u16 w , u16 h , bool force ) {
2014-06-08 15:45:41 -07:00
VirtualFramebuffer old = * vfb ;
2014-06-06 20:11:14 -07:00
if ( force ) {
vfb - > bufferWidth = w ;
vfb - > bufferHeight = h ;
} else {
if ( vfb - > bufferWidth > = w & & vfb - > bufferHeight > = h ) {
return ;
}
// In case it gets thin and wide, don't resize down either side.
vfb - > bufferWidth = std : : max ( vfb - > bufferWidth , w ) ;
vfb - > bufferHeight = std : : max ( vfb - > bufferHeight , h ) ;
}
2015-01-23 10:40:49 +01:00
SetRenderSize ( vfb ) ;
2014-06-07 06:26:03 -07:00
2014-07-05 00:49:30 -07:00
bool trueColor = g_Config . bTrueColor ;
if ( hackForce04154000Download_ & & vfb - > fb_address = = 0x00154000 ) {
2014-08-30 02:05:51 -07:00
trueColor = true ;
2014-07-05 00:49:30 -07:00
}
if ( trueColor ) {
2014-06-06 20:11:14 -07:00
vfb - > colorDepth = FBO_8888 ;
} else {
switch ( vfb - > format ) {
case GE_FORMAT_4444 :
vfb - > colorDepth = FBO_4444 ;
break ;
case GE_FORMAT_5551 :
vfb - > colorDepth = FBO_5551 ;
break ;
case GE_FORMAT_565 :
vfb - > colorDepth = FBO_565 ;
break ;
case GE_FORMAT_8888 :
default :
vfb - > colorDepth = FBO_8888 ;
break ;
}
}
2014-06-07 06:26:03 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
fbo_unbind ( ) ;
2014-06-06 20:11:14 -07:00
if ( ! useBufferedRendering_ ) {
if ( vfb - > fbo ) {
fbo_destroy ( vfb - > fbo ) ;
vfb - > fbo = 0 ;
}
return ;
}
2014-09-09 08:12:42 -07:00
vfb - > fbo = fbo_create ( vfb - > renderWidth , vfb - > renderHeight , 1 , true , ( FBOColorDepth ) vfb - > colorDepth ) ;
2014-06-06 20:11:14 -07:00
if ( old . fbo ) {
2015-11-01 18:05:17 +01:00
INFO_LOG ( SCEGE , " Resizing FBO for %08x : %i x %i x %i " , vfb - > fb_address , w , h , vfb - > format ) ;
2014-06-06 20:11:14 -07:00
if ( vfb - > fbo ) {
2014-09-13 18:46:59 -07:00
fbo_bind_as_render_target ( vfb - > fbo ) ;
2014-06-23 00:17:35 -07:00
ClearBuffer ( ) ;
2014-07-08 23:32:41 -07:00
if ( ! g_Config . bDisableSlowFramebufEffects ) {
2014-09-13 15:12:06 -07:00
BlitFramebuffer ( vfb , 0 , 0 , & old , 0 , 0 , std : : min ( vfb - > bufferWidth , vfb - > width ) , std : : min ( vfb - > height , vfb - > bufferHeight ) , 0 ) ;
2014-07-08 23:32:41 -07:00
}
2014-06-06 20:11:14 -07:00
}
fbo_destroy ( old . fbo ) ;
2014-06-23 00:17:35 -07:00
if ( vfb - > fbo ) {
fbo_bind_as_render_target ( vfb - > fbo ) ;
}
2014-06-06 20:11:14 -07:00
}
2014-06-23 00:17:35 -07:00
if ( ! vfb - > fbo ) {
2014-06-06 20:11:14 -07:00
ERROR_LOG ( SCEGE , " Error creating FBO! %i x %i " , vfb - > renderWidth , vfb - > renderHeight ) ;
}
}
2014-09-09 22:56:54 -07:00
void FramebufferManager : : NotifyRenderFramebufferCreated ( VirtualFramebuffer * vfb ) {
if ( ! useBufferedRendering_ ) {
fbo_unbind ( ) ;
// Let's ignore rendering to targets that have not (yet) been displayed.
gstate_c . skipDrawReason | = SKIPDRAW_NON_DISPLAYED_FB ;
2013-01-30 21:09:53 +01:00
}
2014-09-09 22:56:54 -07:00
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_CREATED ) ;
2014-04-12 23:08:04 -07:00
2014-09-09 22:56:54 -07:00
// Some AMD drivers crash if we don't clear the buffer first?
glDisable ( GL_DITHER ) ; // why?
ClearBuffer ( ) ;
2013-02-01 00:18:23 +01:00
2014-09-09 22:56:54 -07:00
// ugly...
2015-08-05 13:39:05 +02:00
if ( ( gstate_c . curRTWidth ! = vfb - > width | | gstate_c . curRTHeight ! = vfb - > height ) & & shaderManager_ ) {
2014-09-09 22:56:54 -07:00
shaderManager_ - > DirtyUniform ( DIRTY_PROJTHROUGHMATRIX ) ;
2014-05-26 11:40:46 -07:00
}
2014-09-09 22:56:54 -07:00
}
2014-05-26 11:40:46 -07:00
2015-08-05 01:03:49 +02:00
void FramebufferManager : : NotifyRenderFramebufferSwitched ( VirtualFramebuffer * prevVfb , VirtualFramebuffer * vfb , bool isClearingDepth ) {
2014-09-09 22:56:54 -07:00
if ( ShouldDownloadFramebuffer ( vfb ) & & ! vfb - > memoryUpdated ) {
ReadFramebufferToMemory ( vfb , true , 0 , 0 , vfb - > width , vfb - > height ) ;
}
textureCache_ - > ForgetLastTexture ( ) ;
2014-06-17 23:55:09 -07:00
2014-09-09 22:56:54 -07:00
if ( useBufferedRendering_ ) {
if ( vfb - > fbo ) {
fbo_bind_as_render_target ( vfb - > fbo ) ;
2014-07-13 11:59:45 -07:00
} else {
2014-09-09 22:56:54 -07:00
// wtf? This should only happen very briefly when toggling bBufferedRendering
fbo_unbind ( ) ;
2014-07-13 11:59:45 -07:00
}
2014-09-09 22:56:54 -07:00
} else {
if ( vfb - > fbo ) {
// wtf? This should only happen very briefly when toggling bBufferedRendering
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_DESTROYED ) ;
fbo_destroy ( vfb - > fbo ) ;
vfb - > fbo = 0 ;
2013-09-22 21:44:11 -07:00
}
2014-09-09 22:56:54 -07:00
fbo_unbind ( ) ;
2013-09-22 21:44:11 -07:00
2014-09-09 22:56:54 -07:00
// Let's ignore rendering to targets that have not (yet) been displayed.
if ( vfb - > usageFlags & FB_USAGE_DISPLAYED_FRAMEBUFFER ) {
gstate_c . skipDrawReason & = ~ SKIPDRAW_NON_DISPLAYED_FB ;
2013-03-03 13:00:21 +01:00
} else {
2014-09-09 22:56:54 -07:00
gstate_c . skipDrawReason | = SKIPDRAW_NON_DISPLAYED_FB ;
2013-03-03 13:00:21 +01:00
}
2014-09-09 22:56:54 -07:00
}
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_UPDATED ) ;
2013-02-28 23:47:57 +01:00
2015-09-05 16:28:46 -07:00
if ( gl_extensions . IsGLES ) {
// 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.
if ( vfb - > last_frame_render ! = gpuStats . numFlips ) {
ClearBuffer ( ) ;
}
2014-09-09 22:56:54 -07:00
}
2014-01-04 11:23:23 -08:00
2014-09-09 22:56:54 -07:00
// Copy depth pixel value from the read framebuffer to the draw framebuffer
if ( prevVfb & & ! g_Config . bDisableSlowFramebufEffects ) {
2015-08-05 01:03:49 +02:00
if ( ! prevVfb - > fbo | | ! vfb - > fbo | | ! useBufferedRendering_ | | ! prevVfb - > depthUpdated | | isClearingDepth ) {
// 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.
} else {
BlitFramebufferDepth ( prevVfb , vfb ) ;
}
2014-09-09 22:56:54 -07:00
}
if ( vfb - > drawnFormat ! = vfb - > format ) {
// TODO: Might ultimately combine this with the resize step in DoSetRenderFrameBuffer().
ReformatFramebufferFrom ( vfb , vfb - > drawnFormat ) ;
}
2014-06-24 00:14:56 -07:00
2014-09-09 22:56:54 -07:00
// ugly...
2015-08-05 13:39:05 +02:00
if ( ( gstate_c . curRTWidth ! = vfb - > width | | gstate_c . curRTHeight ! = vfb - > height ) & & shaderManager_ ) {
2014-09-09 22:56:54 -07:00
shaderManager_ - > DirtyUniform ( DIRTY_PROJTHROUGHMATRIX ) ;
}
}
void FramebufferManager : : NotifyRenderFramebufferUpdated ( VirtualFramebuffer * vfb , bool vfbFormatChanged ) {
if ( vfbFormatChanged ) {
textureCache_ - > NotifyFramebuffer ( vfb - > fb_address , vfb , NOTIFY_FB_UPDATED ) ;
if ( vfb - > drawnFormat ! = vfb - > format ) {
ReformatFramebufferFrom ( vfb , vfb - > drawnFormat ) ;
}
2013-03-16 00:40:37 +01:00
}
// ugly...
2015-08-05 13:39:05 +02:00
if ( ( gstate_c . curRTWidth ! = vfb - > width | | gstate_c . curRTHeight ! = vfb - > height ) & & shaderManager_ ) {
2013-03-16 00:40:37 +01:00
shaderManager_ - > DirtyUniform ( DIRTY_PROJTHROUGHMATRIX ) ;
2013-01-30 21:09:53 +01:00
}
}
2013-10-09 19:00:35 +02:00
void FramebufferManager : : SetLineWidth ( ) {
# ifndef USING_GLES2
if ( g_Config . iInternalResolution = = 0 ) {
2015-09-19 16:19:03 +02:00
glLineWidth ( std : : max ( 1 , ( int ) ( renderWidth_ / 480 ) ) ) ;
glPointSize ( std : : max ( 1.0f , ( float ) ( renderWidth_ / 480.f ) ) ) ;
2013-10-09 19:00:35 +02:00
} else {
glLineWidth ( g_Config . iInternalResolution ) ;
glPointSize ( ( float ) g_Config . iInternalResolution ) ;
}
# endif
}
2014-09-07 08:47:04 -07:00
void FramebufferManager : : ReformatFramebufferFrom ( VirtualFramebuffer * vfb , GEBufferFormat old ) {
2014-09-11 23:30:42 -07:00
if ( ! useBufferedRendering_ | | ! vfb - > fbo ) {
2014-09-07 08:47:04 -07:00
return ;
}
fbo_bind_as_render_target ( vfb - > fbo ) ;
// Technically, we should at this point re-interpret the bytes of the old format to the new.
// That might get tricky, and could cause unnecessary slowness in some games.
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
// (it uses 565 to write zeros to the buffer, than 4444 to actually render the shadow.)
//
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
// to exactly reproduce in 4444 and 8888 formats.
if ( old = = GE_FORMAT_565 ) {
glstate . scissorTest . disable ( ) ;
glstate . depthWrite . set ( GL_FALSE ) ;
glstate . colorMask . set ( false , false , false , true ) ;
glstate . stencilFunc . set ( GL_ALWAYS , 0 , 0 ) ;
glstate . stencilMask . set ( 0xFF ) ;
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
glClearStencil ( 0 ) ;
glClear ( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
}
RebindFramebuffer ( ) ;
}
2014-09-14 11:54:55 -07:00
void FramebufferManager : : BlitFramebufferDepth ( VirtualFramebuffer * src , VirtualFramebuffer * dst ) {
if ( src - > z_address = = dst - > z_address & &
src - > z_stride ! = 0 & & dst - > z_stride ! = 0 & &
src - > renderWidth = = dst - > renderWidth & &
src - > renderHeight = = dst - > renderHeight ) {
2014-06-11 00:40:47 -07:00
2015-09-05 22:40:45 +02:00
if ( gstate_c . Supports ( GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT ) ) {
// Only use NV if ARB isn't supported.
bool useNV = ! gstate_c . Supports ( GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT ) ;
2014-01-20 22:02:20 +08:00
2015-07-26 22:54:18 +02:00
// Let's only do this if not clearing depth.
fbo_bind_for_read ( src - > fbo ) ;
2015-11-03 23:02:02 -08:00
glstate . scissorTest . force ( false ) ;
2014-05-10 12:26:53 +08:00
2015-07-26 22:54:18 +02:00
if ( useNV ) {
2015-09-05 22:40:45 +02:00
# if defined(USING_GLES2) && defined(ANDROID) // We only support this extension on Android, it's not even available on PC.
2015-07-26 22:54:18 +02:00
glBlitFramebufferNV ( 0 , 0 , src - > renderWidth , src - > renderHeight , 0 , 0 , dst - > renderWidth , dst - > renderHeight , GL_DEPTH_BUFFER_BIT , GL_NEAREST ) ;
2014-06-10 15:11:25 +10:00
# endif // defined(USING_GLES2) && defined(ANDROID)
2015-09-05 22:40:45 +02:00
} else {
2015-07-26 22:54:18 +02:00
glBlitFramebuffer ( 0 , 0 , src - > renderWidth , src - > renderHeight , 0 , 0 , dst - > renderWidth , dst - > renderHeight , GL_DEPTH_BUFFER_BIT , GL_NEAREST ) ;
2015-09-05 22:40:45 +02:00
}
2015-07-26 22:54:18 +02:00
// If we set dst->depthUpdated here, our optimization above would be pointless.
2014-05-27 02:00:28 -07:00
2015-07-26 22:54:18 +02:00
glstate . scissorTest . restore ( ) ;
2014-01-20 22:02:20 +08:00
}
}
2014-01-19 17:28:11 -08:00
}
2014-05-31 22:41:41 -07:00
FBO * FramebufferManager : : GetTempFBO ( u16 w , u16 h , FBOColorDepth depth ) {
2015-01-19 08:41:53 -08:00
u64 key = ( ( u64 ) depth < < 32 ) | ( ( u32 ) w < < 16 ) | h ;
2014-05-31 22:41:41 -07:00
auto it = tempFBOs_ . find ( key ) ;
if ( it ! = tempFBOs_ . end ( ) ) {
it - > second . last_frame_used = gpuStats . numFlips ;
return it - > second . fbo ;
}
2014-08-27 23:05:21 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
2014-05-31 22:41:41 -07:00
FBO * fbo = fbo_create ( w , h , 1 , false , depth ) ;
if ( ! fbo )
return fbo ;
2014-09-13 18:46:59 -07:00
fbo_bind_as_render_target ( fbo ) ;
2015-11-03 22:37:19 -08:00
ClearBuffer ( true ) ;
2014-05-31 22:41:41 -07:00
const TempFBO info = { fbo , gpuStats . numFlips } ;
tempFBOs_ [ key ] = info ;
return fbo ;
}
2015-09-13 11:14:51 -07:00
void FramebufferManager : : BindFramebufferColor ( int stage , u32 fbRawAddress , VirtualFramebuffer * framebuffer , int flags ) {
2014-05-11 09:51:35 -07:00
if ( framebuffer = = NULL ) {
framebuffer = currentRenderVfb_ ;
}
2014-09-21 12:11:17 -07:00
if ( stage ! = GL_TEXTURE0 ) {
glActiveTexture ( stage ) ;
}
2014-01-19 17:28:11 -08:00
if ( ! framebuffer - > fbo | | ! useBufferedRendering_ ) {
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2014-09-21 12:11:17 -07:00
glActiveTexture ( GL_TEXTURE0 ) ;
2014-01-19 17:28:11 -08:00
gstate_c . skipDrawReason | = SKIPDRAW_BAD_FB_TEXTURE ;
return ;
}
2014-05-23 08:48:29 -07: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.
2015-09-13 11:14:51 -07:00
bool skipCopy = ( flags & BINDFBCOLOR_MAY_COPY ) = = 0 ;
2014-07-08 23:32:41 -07:00
if ( GPUStepping : : IsStepping ( ) | | g_Config . bDisableSlowFramebufEffects ) {
2014-06-13 08:35:12 -07:00
skipCopy = true ;
}
2015-08-05 12:13:14 +02:00
if ( ! skipCopy & & currentRenderVfb_ & & framebuffer - > fb_address = = fbRawAddress ) {
2014-05-30 08:53:22 -07:00
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
2014-09-09 08:12:42 -07:00
FBO * renderCopy = GetTempFBO ( framebuffer - > renderWidth , framebuffer - > renderHeight , ( FBOColorDepth ) framebuffer - > colorDepth ) ;
2014-05-31 22:41:41 -07:00
if ( renderCopy ) {
VirtualFramebuffer copyInfo = * framebuffer ;
copyInfo . fbo = renderCopy ;
2015-09-13 11:14:51 -07:00
int x = 0 ;
int y = 0 ;
int w = framebuffer - > drawnWidth ;
int h = framebuffer - > drawnHeight ;
// If max is not > min, we probably could not detect it. Skip.
// See the vertex decoder, where this is updated.
2015-11-09 23:28:15 -08:00
if ( ( flags & BINDFBCOLOR_MAY_COPY_WITH_UV ) = = BINDFBCOLOR_MAY_COPY_WITH_UV & & gstate_c . vertBounds . maxU > gstate_c . vertBounds . minU ) {
2015-09-13 14:52:10 -07:00
x = gstate_c . vertBounds . minU ;
y = gstate_c . vertBounds . minV ;
w = gstate_c . vertBounds . maxU - x ;
h = gstate_c . vertBounds . maxV - y ;
2015-09-12 19:43:02 -07:00
// If we bound a framebuffer, apply the byte offset as pixels to the copy too.
if ( flags & BINDFBCOLOR_APPLY_TEX_OFFSET ) {
x + = gstate_c . curTextureXOffset ;
y + = gstate_c . curTextureYOffset ;
}
2015-09-13 11:14:51 -07:00
}
2015-11-01 13:32:03 +01:00
BlitFramebuffer ( & copyInfo , x , y , framebuffer , x , y , w , h , 0 ) ;
2015-09-13 11:14:51 -07:00
2014-05-31 22:41:41 -07:00
fbo_bind_color_as_texture ( renderCopy , 0 ) ;
} else {
fbo_bind_color_as_texture ( framebuffer - > fbo , 0 ) ;
2014-05-30 08:53:22 -07:00
}
2014-01-19 17:28:11 -08:00
} else {
fbo_bind_color_as_texture ( framebuffer - > fbo , 0 ) ;
}
2014-09-21 12:11:17 -07:00
2015-03-08 13:13:04 -07:00
if ( stage ! = GL_TEXTURE0 ) {
2014-09-21 12:11:17 -07:00
glActiveTexture ( stage ) ;
}
2014-01-20 22:02:20 +08:00
}
2015-01-22 12:48:25 +01:00
struct CardboardSettings * FramebufferManager : : GetCardboardSettings ( struct CardboardSettings * cardboardSettings ) {
2015-01-24 20:01:09 +01:00
if ( cardboardSettings ) {
// Calculate Cardboard Settings
float cardboardScreenScale = g_Config . iCardboardScreenSize / 100.0f ;
2015-09-19 16:19:03 +02:00
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale ;
float cardboardScreenHeight = pixelHeight_ / 2.0f * cardboardScreenScale ;
float cardboardMaxXShift = ( pixelWidth_ / 2.0f - cardboardScreenWidth ) / 2.0f ;
2015-01-24 20:01:09 +01:00
float cardboardUserXShift = g_Config . iCardboardXShift / 100.0f * cardboardMaxXShift ;
float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift ;
2015-09-19 16:19:03 +02:00
float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift ;
float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f ;
2015-01-24 20:01:09 +01:00
float cardboardUserYShift = g_Config . iCardboardYShift / 100.0f * cardboardMaxYShift ;
float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift ;
// Copy current Settings into Structure
cardboardSettings - > enabled = g_Config . bEnableCardboard ;
cardboardSettings - > leftEyeXPosition = cardboardLeftEyeX ;
cardboardSettings - > rightEyeXPosition = cardboardRightEyeX ;
cardboardSettings - > screenYPosition = cardboardScreenY ;
cardboardSettings - > screenWidth = cardboardScreenWidth ;
cardboardSettings - > screenHeight = cardboardScreenHeight ;
}
return cardboardSettings ;
2015-01-22 12:48:25 +01:00
}
2013-01-30 21:09:53 +01:00
void FramebufferManager : : CopyDisplayToOutput ( ) {
fbo_unbind ( ) ;
2015-09-19 16:19:03 +02:00
glstate . viewport . set ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2015-11-13 01:14:56 +01:00
2015-12-30 17:44:32 -08:00
currentRenderVfb_ = 0 ;
if ( displayFramebufPtr_ = = 0 ) {
DEBUG_LOG ( SCEGE , " Display disabled, displaying only black " ) ;
// No framebuffer to display! Clear to black.
ClearBuffer ( ) ;
return ;
}
2015-11-13 01:14:56 +01:00
if ( useBufferedRendering_ ) {
glClearColor ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
2015-11-14 09:40:36 +01:00
# ifdef USING_GLES2
glClearDepthf ( 0.0f ) ;
# else
glClearDepth ( 0.0 ) ;
# endif
2015-11-13 01:14:56 +01:00
glClearStencil ( 0 ) ;
// Hardly necessary to clear depth and stencil I guess...
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ) ;
}
2014-06-06 20:54:34 -07:00
u32 offsetX = 0 ;
u32 offsetY = 0 ;
2015-01-24 20:01:09 +01:00
struct CardboardSettings cardboardSettings ;
GetCardboardSettings ( & cardboardSettings ) ;
2015-01-22 12:48:25 +01:00
2013-09-21 18:52:30 +02:00
VirtualFramebuffer * vfb = GetVFBAt ( displayFramebufPtr_ ) ;
2014-06-06 20:54:34 -07:00
if ( ! vfb ) {
// Let's search for a framebuf within this range.
const u32 addr = ( displayFramebufPtr_ & 0x03FFFFFF ) | 0x04000000 ;
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * v = vfbs_ [ i ] ;
const u32 v_addr = ( v - > fb_address & 0x03FFFFFF ) | 0x04000000 ;
const u32 v_size = FramebufferByteSize ( v ) ;
if ( addr > = v_addr & & addr < v_addr + v_size ) {
const u32 dstBpp = v - > format = = GE_FORMAT_8888 ? 4 : 2 ;
const u32 v_offsetX = ( ( addr - v_addr ) / dstBpp ) % v - > fb_stride ;
const u32 v_offsetY = ( ( addr - v_addr ) / dstBpp ) / v - > fb_stride ;
// We have enough space there for the display, right?
if ( v_offsetX + 480 > ( u32 ) v - > fb_stride | | v - > bufferHeight < v_offsetY + 272 ) {
continue ;
}
// Check for the closest one.
if ( offsetY = = 0 | | offsetY > v_offsetY ) {
offsetX = v_offsetX ;
offsetY = v_offsetY ;
vfb = v ;
}
}
}
if ( vfb ) {
// Okay, we found one above.
INFO_LOG_REPORT_ONCE ( displayoffset , HLE , " Rendering from framebuf with offset %08x -> %08x+%dx%d " , addr , vfb - > fb_address , offsetX , offsetY ) ;
}
}
2014-06-06 21:11:58 -07:00
if ( vfb & & vfb - > format ! = displayFormat_ ) {
if ( vfb - > last_frame_render + FBO_OLD_AGE < gpuStats . numFlips ) {
// The game probably switched formats on us.
vfb - > format = displayFormat_ ;
} else {
vfb = 0 ;
}
}
2013-01-30 21:09:53 +01:00
if ( ! vfb ) {
2013-09-21 18:52:30 +02:00
if ( Memory : : IsValidAddress ( displayFramebufPtr_ ) ) {
2013-03-15 22:50:35 +01:00
// The game is displaying something directly from RAM. In GTA, it's decoded video.
2013-09-21 18:52:30 +02:00
// First check that it's not a known RAM copy of a VRAM framebuffer though, as in MotoGP
2014-05-09 21:17:54 +02:00
for ( auto iter = knownFramebufferRAMCopies_ . begin ( ) ; iter ! = knownFramebufferRAMCopies_ . end ( ) ; + + iter ) {
2013-09-21 18:52:30 +02:00
if ( iter - > second = = displayFramebufPtr_ ) {
vfb = GetVFBAt ( iter - > first ) ;
}
}
if ( ! vfb ) {
2013-12-04 11:46:42 +01:00
// Just a pointer to plain memory to draw. We should create a framebuffer, then draw to it.
2015-11-01 15:34:53 +01:00
DrawFramebufferToOutput ( Memory : : GetPointer ( displayFramebufPtr_ ) , displayFormat_ , displayStride_ , true ) ;
2013-09-21 18:52:30 +02:00
return ;
}
2013-03-15 22:50:35 +01:00
} else {
2013-09-07 22:02:55 +02:00
DEBUG_LOG ( SCEGE , " Found no FBO to display! displayFBPtr = %08x " , displayFramebufPtr_ ) ;
2013-03-15 22:50:35 +01:00
// No framebuffer to display! Clear to black.
2013-08-15 03:32:43 +08:00
ClearBuffer ( ) ;
2013-09-21 18:52:30 +02:00
return ;
2013-03-15 22:50:35 +01:00
}
2013-01-30 21:09:53 +01:00
}
2013-03-03 13:00:21 +01:00
vfb - > usageFlags | = FB_USAGE_DISPLAYED_FRAMEBUFFER ;
2015-03-14 14:58:32 -07:00
vfb - > last_frame_displayed = gpuStats . numFlips ;
2013-03-03 13:00:21 +01:00
vfb - > dirtyAfterDisplay = false ;
2013-08-16 01:00:26 +02:00
vfb - > reallyDirtyAfterDisplay = false ;
2013-03-03 13:00:21 +01:00
2013-04-12 07:09:45 -07:00
if ( prevDisplayFramebuf_ ! = displayFramebuf_ ) {
prevPrevDisplayFramebuf_ = prevDisplayFramebuf_ ;
}
if ( displayFramebuf_ ! = vfb ) {
prevDisplayFramebuf_ = displayFramebuf_ ;
}
2013-01-30 21:09:53 +01:00
displayFramebuf_ = vfb ;
2013-03-09 13:46:11 +08:00
if ( vfb - > fbo ) {
2013-09-07 22:02:55 +02:00
DEBUG_LOG ( SCEGE , " Displaying FBO %08x " , vfb - > fb_address ) ;
2013-08-15 03:32:43 +08:00
DisableState ( ) ;
2013-03-03 13:00:21 +01:00
2013-10-01 16:20:09 +02:00
GLuint colorTexture = fbo_get_color_texture ( vfb - > fbo ) ;
2015-05-12 22:44:02 +02:00
int uvRotation = ( g_Config . iRenderingMode ! = FB_NON_BUFFERED_MODE ) ? g_Config . iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL ;
2013-10-22 12:17:40 +02:00
// Output coordinates
float x , y , w , h ;
2015-11-03 00:24:19 +01:00
CenterDisplayOutputRect ( & x , & y , & w , & h , 480.0f , 272.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ , uvRotation ) ;
2013-10-22 12:17:40 +02:00
2013-10-09 11:01:52 +02:00
// TODO ES3: Use glInvalidateFramebuffer to discard depth/stencil data at the end of frame.
2015-11-01 15:34:53 +01:00
float u0 = offsetX / ( float ) vfb - > bufferWidth ;
float v0 = offsetY / ( float ) vfb - > bufferHeight ;
float u1 = ( 480.0f + offsetX ) / ( float ) vfb - > bufferWidth ;
float v1 = ( 272.0f + offsetY ) / ( float ) vfb - > bufferHeight ;
2014-06-06 20:54:34 -07:00
2013-10-22 12:17:40 +02:00
if ( ! usePostShader_ ) {
2015-11-01 15:34:53 +01:00
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
// flip V.
std : : swap ( v0 , v1 ) ;
2015-01-24 20:01:09 +01:00
if ( cardboardSettings . enabled ) {
// Left Eye Image
glstate . viewport . set ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
// Right Eye Image
glstate . viewport . set ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
} else {
// Fullscreen Image
2015-09-19 16:19:03 +02:00
glstate . viewport . set ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , uvRotation ) ;
2015-01-24 20:01:09 +01:00
}
2013-10-22 12:17:40 +02:00
} else if ( usePostShader_ & & extraFBOs_ . size ( ) = = 1 & & ! postShaderAtOutputResolution_ ) {
2013-10-30 00:12:19 +05:00
// An additional pass, post-processing shader to the extra FBO.
2013-09-28 22:42:13 +02: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 ) ;
2015-10-31 16:02:25 +01:00
shaderManager_ - > DirtyLastShader ( ) ; // dirty lastShader_
glsl_bind ( postShaderProgram_ ) ;
2015-11-02 22:22:48 +01:00
UpdatePostShaderUniforms ( vfb - > bufferWidth , vfb - > bufferHeight , renderWidth_ , renderHeight_ ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , 0 , 0 , fbo_w , fbo_h , fbo_w , fbo_h , 0.0f , 0.0f , 1.0f , 1.0f , postShaderProgram_ , ROTATION_LOCKED_HORIZONTAL ) ;
2013-09-28 22:42:13 +02:00
fbo_unbind ( ) ;
2013-10-30 00:12:19 +05:00
// Use the extra FBO, with applied post-processing shader, as a texture.
2013-10-01 16:20:09 +02:00
// fbo_bind_color_as_texture(extraFBOs_[0], 0);
2013-10-22 13:40:51 +02:00
if ( extraFBOs_ . size ( ) = = 0 ) {
ERROR_LOG ( G3D , " WTF? " ) ;
return ;
}
2013-10-01 16:20:09 +02:00
colorTexture = fbo_get_color_texture ( extraFBOs_ [ 0 ] ) ;
2015-01-21 23:57:20 +01:00
2015-11-01 15:34:53 +01:00
// We are doing the DrawActiveTexture call directly to the backbuffer after here. Hence, we must
// flip V.
std : : swap ( v0 , v1 ) ;
2015-01-24 20:01:09 +01:00
if ( g_Config . bEnableCardboard ) {
// Left Eye Image
glstate . viewport . set ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
// Right Eye Image
glstate . viewport . set ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
} else {
// Fullscreen Image
2015-09-19 16:19:03 +02:00
glstate . viewport . set ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , uvRotation ) ;
2015-01-24 20:01:09 +01:00
}
2015-03-14 16:16:05 -07:00
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
fbo_bind_as_render_target ( extraFBOs_ [ 0 ] ) ;
GLenum attachments [ 3 ] = { GL_COLOR_ATTACHMENT0 , GL_DEPTH_ATTACHMENT , GL_STENCIL_ATTACHMENT } ;
glInvalidateFramebuffer ( GL_FRAMEBUFFER , 3 , attachments ) ;
}
2013-10-22 12:17:40 +02:00
} else {
2015-11-01 15:34:53 +01:00
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
// flip V.
std : : swap ( v0 , v1 ) ;
2015-10-31 16:02:25 +01:00
shaderManager_ - > DirtyLastShader ( ) ; // dirty lastShader_
glsl_bind ( postShaderProgram_ ) ;
2015-11-02 22:22:48 +01:00
UpdatePostShaderUniforms ( vfb - > bufferWidth , vfb - > bufferHeight , vfb - > renderWidth , vfb - > renderHeight ) ;
2015-01-24 20:01:09 +01:00
if ( g_Config . bEnableCardboard ) {
// Left Eye Image
glstate . viewport . set ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
// Right Eye Image
glstate . viewport . set ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , nullptr , ROTATION_LOCKED_HORIZONTAL ) ;
2015-01-24 20:01:09 +01:00
} else {
// Fullscreen Image
2015-09-19 16:19:03 +02:00
glstate . viewport . set ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2015-10-31 23:59:23 +01:00
DrawActiveTexture ( colorTexture , x , y , w , h , ( float ) pixelWidth_ , ( float ) pixelHeight_ , u0 , v0 , u1 , v1 , postShaderProgram_ , uvRotation ) ;
2015-01-24 20:01:09 +01:00
}
2013-09-28 22:42:13 +02:00
}
2013-03-11 22:22:22 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2013-03-03 13:00:21 +01:00
}
}
2013-01-30 21:09:53 +01:00
2014-05-08 16:09:55 +02:00
void FramebufferManager : : ReadFramebufferToMemory ( VirtualFramebuffer * vfb , bool sync , int x , int y , int w , int h ) {
2015-07-12 00:44:11 +02:00
PROFILE_THIS_SCOPE ( " gpu-readback " ) ;
2013-10-09 11:01:52 +02:00
if ( sync ) {
2015-11-01 18:05:17 +01:00
// flush async just in case when we go for synchronous update
// Doesn't actually pack when sent a null argument.
PackFramebufferAsync_ ( nullptr ) ;
2013-08-12 02:30:17 +08:00
}
2013-10-09 11:01:52 +02:00
if ( vfb ) {
2015-11-01 18:05:17 +01:00
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
2013-06-28 14:48:36 +01:00
// 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 ] ;
2014-06-21 08:30:45 -07:00
if ( v - > fb_address = = vfb - > fb_address & & v - > format = = vfb - > format ) {
2013-06-28 14:48:36 +01:00
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 13:50:35 +01:00
}
}
2013-06-28 14:48:36 +01:00
}
2013-06-25 13:50:35 +01:00
2013-06-28 14:48:36 +01:00
// Create a new fbo if none was found for the size
2013-10-09 11:01:52 +02:00
if ( ! nvfb ) {
2013-06-28 14:48:36 +01: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 ;
2014-06-03 22:29:42 -07:00
nvfb - > renderWidth = vfb - > bufferWidth ;
nvfb - > renderHeight = vfb - > bufferHeight ;
2013-06-28 14:48:36 +01:00
nvfb - > bufferWidth = vfb - > bufferWidth ;
nvfb - > bufferHeight = vfb - > bufferHeight ;
nvfb - > format = vfb - > format ;
2014-09-08 07:40:43 -07:00
nvfb - > drawnWidth = vfb - > drawnWidth ;
nvfb - > drawnHeight = vfb - > drawnHeight ;
nvfb - > drawnFormat = vfb - > format ;
2013-06-28 14:48:36 +01:00
nvfb - > usageFlags = FB_USAGE_RENDERTARGET ;
nvfb - > dirtyAfterDisplay = true ;
2013-09-22 17:03:39 +08: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 20:11:20 +02:00
case GE_FORMAT_565 :
2013-09-22 17:03:39 +08:00
nvfb - > colorDepth = FBO_565 ;
break ;
case GE_FORMAT_8888 :
default :
nvfb - > colorDepth = FBO_8888 ;
break ;
2013-06-28 14:48:36 +01:00
}
2015-09-05 19:58:47 +02:00
if ( gstate_c . Supports ( GPU_PREFER_CPU_DOWNLOAD ) ) {
2014-08-30 02:05:51 -07:00
nvfb - > colorDepth = vfb - > colorDepth ;
}
2013-06-25 13:50:35 +01:00
2014-08-27 23:05:21 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
2014-09-09 08:12:42 -07:00
nvfb - > fbo = fbo_create ( nvfb - > width , nvfb - > height , 1 , false , ( FBOColorDepth ) nvfb - > colorDepth ) ;
2013-06-28 14:48:36 +01:00
if ( ! ( nvfb - > fbo ) ) {
2013-09-07 22:02:55 +02:00
ERROR_LOG ( SCEGE , " Error creating FBO! %i x %i " , nvfb - > renderWidth , nvfb - > renderHeight ) ;
2015-01-18 13:18:17 -08:00
delete nvfb ;
2013-08-12 02:30:17 +08:00
return ;
2013-06-25 13:50:35 +01:00
}
2013-09-01 11:55:03 -07:00
nvfb - > last_frame_render = gpuStats . numFlips ;
2013-06-28 14:48:36 +01:00
bvfbs_ . push_back ( nvfb ) ;
2014-09-13 18:46:59 -07:00
fbo_bind_as_render_target ( nvfb - > fbo ) ;
2013-08-15 03:32:43 +08:00
ClearBuffer ( ) ;
2014-07-04 14:01:28 -07:00
glDisable ( GL_DITHER ) ;
2013-06-28 14:48:36 +01:00
} else {
nvfb - > usageFlags | = FB_USAGE_RENDERTARGET ;
2014-05-27 02:00:49 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
2013-09-01 11:55:03 -07:00
nvfb - > last_frame_render = gpuStats . numFlips ;
2013-06-28 14:48:36 +01:00
nvfb - > dirtyAfterDisplay = true ;
2015-12-06 09:08:38 -08:00
if ( gl_extensions . IsGLES ) {
if ( nvfb - > fbo ) {
fbo_bind_as_render_target ( nvfb - > fbo ) ;
}
2013-08-12 02:30:17 +08:00
2015-12-06 09:08:38 -08: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.
if ( nvfb - > last_frame_render ! = gpuStats . numFlips ) {
ClearBuffer ( ) ;
}
2013-06-25 13:50:35 +01:00
}
2013-06-28 14:48:36 +01:00
}
2013-06-25 13:50:35 +01:00
2014-05-25 18:18:26 -07: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 ;
}
}
2015-11-01 13:32:03 +01:00
BlitFramebuffer ( nvfb , x , y , vfb , x , y , w , h , 0 ) ;
2013-06-25 13:50:35 +01:00
2014-05-08 10:46:19 +02:00
// PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels
// PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs
2015-12-06 09:08:38 -08:00
if ( gl_extensions . IsGLES ) {
PackFramebufferSync_ ( nvfb , x , y , w , h ) ;
} else {
// TODO: Can we fall back to sync without these?
if ( gl_extensions . ARB_pixel_buffer_object & & gstate_c . Supports ( GPU_SUPPORTS_OES_TEXTURE_NPOT ) ) {
if ( ! sync ) {
PackFramebufferAsync_ ( nvfb ) ;
} else {
PackFramebufferSync_ ( nvfb , x , y , w , h ) ;
}
2013-09-29 10:30:25 +08:00
}
2013-08-12 02:30:17 +08:00
}
2014-05-09 20:48:36 +02:00
2014-06-11 00:54:54 -07:00
RebindFramebuffer ( ) ;
2013-06-28 14:48:36 +01:00
}
}
2015-11-01 13:32:03 +01:00
void FramebufferManager : : BlitFramebuffer ( VirtualFramebuffer * dst , int dstX , int dstY , VirtualFramebuffer * src , int srcX , int srcY , int w , int h , int bpp ) {
2014-07-09 23:40:29 -07:00
if ( ! dst - > fbo | | ! src - > fbo | | ! useBufferedRendering_ ) {
// This can happen if they recently switched from non-buffered.
2014-05-08 15:16:33 +02:00
fbo_unbind ( ) ;
return ;
}
2015-09-05 16:28:59 -07:00
bool useBlit = gstate_c . Supports ( GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT ) ;
bool useNV = useBlit & & ! gstate_c . Supports ( GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT ) ;
2013-10-09 11:01:52 +02:00
2014-06-05 00:14:31 -07:00
float srcXFactor = useBlit ? ( float ) src - > renderWidth / ( float ) src - > bufferWidth : 1.0f ;
float srcYFactor = useBlit ? ( float ) src - > renderHeight / ( float ) src - > bufferHeight : 1.0f ;
const 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 ;
2015-11-01 22:23:37 +01:00
int srcY1 = srcY * srcYFactor ;
int srcY2 = ( srcY + h ) * srcYFactor ;
2014-06-05 00:14:31 -07:00
float dstXFactor = useBlit ? ( float ) dst - > renderWidth / ( float ) dst - > bufferWidth : 1.0f ;
float dstYFactor = useBlit ? ( float ) dst - > renderHeight / ( float ) dst - > bufferHeight : 1.0f ;
const 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 ;
2015-11-01 00:13:37 +01:00
int dstY1 = dstY * dstYFactor ;
int dstY2 = ( dstY + h ) * dstYFactor ;
2014-05-25 15:13:19 -07:00
2015-12-30 10:27:18 -08:00
if ( src = = dst & & srcX = = dstX & & srcY = = dstY ) {
// Let's just skip a copy where the destination is equal to the source.
WARN_LOG_REPORT_ONCE ( blitSame , G3D , " Skipped blit with equal dst and src " ) ;
return ;
}
2015-12-06 10:39:21 -08:00
if ( gstate_c . Supports ( GPU_SUPPORTS_ANY_COPY_IMAGE ) ) {
2015-12-28 22:23:07 -08:00
// glBlitFramebuffer can clip, but glCopyImageSubData is more restricted.
// In case the src goes outside, we just skip the optimization in that case.
const bool sameSize = dstX2 - dstX1 = = srcX2 - srcX1 & & dstY2 - dstY1 = = srcY2 - srcY1 ;
2015-12-30 10:27:18 -08:00
const bool sameDepth = dst - > colorDepth = = src - > colorDepth ;
2015-12-28 22:23:07 -08:00
const bool srcInsideBounds = srcX2 < = src - > renderWidth & & srcY2 < = src - > renderHeight ;
const bool dstInsideBounds = dstX2 < = dst - > renderWidth & & dstY2 < = dst - > renderHeight ;
2015-12-30 10:27:18 -08:00
const bool xOverlap = src = = dst & & srcX2 > dstX1 & & srcX1 < dstX2 ;
const bool yOverlap = src = = dst & & srcY2 > dstY1 & & srcY1 < dstY2 ;
if ( sameSize & & sameDepth & & srcInsideBounds & & dstInsideBounds & & ! ( xOverlap & & yOverlap ) ) {
2015-12-06 10:39:21 -08:00
# if defined(USING_GLES2)
# ifndef IOS
glCopyImageSubDataOES (
fbo_get_color_texture ( src - > fbo ) , GL_TEXTURE_2D , 0 , srcX1 , srcY1 , 0 ,
fbo_get_color_texture ( dst - > fbo ) , GL_TEXTURE_2D , 0 , dstX1 , dstY1 , 0 ,
dstX2 - dstX1 , dstY2 - dstY1 , 1 ) ;
return ;
# endif
# else
if ( gl_extensions . ARB_copy_image ) {
glCopyImageSubData (
fbo_get_color_texture ( src - > fbo ) , GL_TEXTURE_2D , 0 , srcX1 , srcY1 , 0 ,
fbo_get_color_texture ( dst - > fbo ) , GL_TEXTURE_2D , 0 , dstX1 , dstY1 , 0 ,
dstX2 - dstX1 , dstY2 - dstY1 , 1 ) ;
return ;
} else if ( gl_extensions . NV_copy_image ) {
// Older, pre GL 4.x NVIDIA cards.
glCopyImageSubDataNV (
fbo_get_color_texture ( src - > fbo ) , GL_TEXTURE_2D , 0 , srcX1 , srcY1 , 0 ,
fbo_get_color_texture ( dst - > fbo ) , GL_TEXTURE_2D , 0 , dstX1 , dstY1 , 0 ,
dstX2 - dstX1 , dstY2 - dstY1 , 1 ) ;
return ;
}
# endif
}
}
fbo_bind_as_render_target ( dst - > fbo ) ;
glstate . scissorTest . force ( false ) ;
2014-06-05 00:14:31 -07:00
if ( useBlit ) {
2014-05-25 15:13:19 -07: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-06-05 00:14:31 -07:00
} else {
2014-06-10 15:11:25 +10:00
# if defined(USING_GLES2) && defined(ANDROID) // We only support this extension on Android, it's not even available on PC.
2014-05-25 15:13:19 -07:00
glBlitFramebufferNV ( srcX1 , srcY1 , srcX2 , srcY2 , dstX1 , dstY1 , dstX2 , dstY2 , GL_COLOR_BUFFER_BIT , GL_NEAREST ) ;
2014-06-10 15:11:25 +10:00
# endif // defined(USING_GLES2) && defined(ANDROID)
2014-06-05 00:14:31 -07:00
}
2013-06-28 14:48:36 +01:00
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2014-05-09 22:01:30 +08: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 ( ) ;
2015-11-15 13:12:29 -08:00
glstate . viewport . force ( 0 , 0 , dst - > renderWidth , dst - > renderHeight ) ;
glstate . blend . force ( false ) ;
glstate . cullFace . force ( false ) ;
glstate . depthTest . force ( false ) ;
glstate . stencilTest . force ( false ) ;
# if !defined(USING_GLES2)
glstate . colorLogicOp . force ( false ) ;
# endif
glstate . colorMask . force ( true , true , true , true ) ;
glstate . stencilMask . force ( 0xFF ) ;
2014-05-09 22:01:30 +08:00
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
// Should maybe revamp that interface.
2014-06-01 21:20:04 -07:00
float srcW = src - > bufferWidth ;
float srcH = src - > bufferHeight ;
2015-11-01 22:40:31 +01:00
DrawActiveTexture ( 0 , dstX1 , dstY1 , w * dstXFactor , h , dst - > bufferWidth , dst - > bufferHeight , srcX1 / srcW , srcY1 / srcH , srcX2 / srcW , srcY2 / srcH , draw2dprogram_ , ROTATION_LOCKED_HORIZONTAL ) ;
2014-05-09 22:01:30 +08:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2014-05-27 02:00:49 -07:00
textureCache_ - > ForgetLastTexture ( ) ;
2014-08-27 23:07:11 -07:00
glstate . viewport . restore ( ) ;
2015-11-15 13:12:29 -08:00
glstate . blend . restore ( ) ;
glstate . cullFace . restore ( ) ;
glstate . depthTest . restore ( ) ;
glstate . stencilTest . restore ( ) ;
# if !defined(USING_GLES2)
glstate . colorLogicOp . restore ( ) ;
# endif
glstate . colorMask . restore ( ) ;
glstate . stencilMask . restore ( ) ;
2014-05-09 22:01:30 +08:00
}
2013-10-09 11:01:52 +02:00
2014-05-27 02:00:28 -07:00
glstate . scissorTest . restore ( ) ;
2013-06-28 14:48:36 +01:00
}
2013-07-06 11:09:08 +02:00
// TODO: SSE/NEON
2013-10-23 14:46:04 +02:00
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
2014-09-13 16:37:59 -07:00
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 dstStride , u32 srcStride , u32 width , u32 height , GEBufferFormat format ) {
2014-06-15 19:33:32 -07:00
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
const u32 * src32 = ( const u32 * ) src ;
2015-01-22 19:53:32 +01:00
2013-10-09 11:01:52 +02:00
if ( format = = GE_FORMAT_8888 ) {
2014-06-15 19:33:32 -07:00
u32 * dst32 = ( u32 * ) dst ;
2013-10-09 11:01:52 +02:00
if ( src = = dst ) {
2013-07-02 14:08:59 +01:00
return ;
2014-05-04 00:18:01 -07:00
} else if ( UseBGRA8888 ( ) ) {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
ConvertBGRA8888ToRGBA8888 ( dst32 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst32 + = dstStride ;
2014-06-15 19:33:32 -07:00
}
2014-05-08 10:46:19 +02:00
} else {
// Here let's assume they don't intersect
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
memcpy ( dst32 , src32 , width * 4 ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst32 + = dstStride ;
2014-06-15 19:33:32 -07:00
}
2013-07-02 14:08:59 +01:00
}
2014-05-08 10:46:19 +02:00
} else {
// But here it shouldn't matter if they do intersect
2013-07-05 02:31:31 +01:00
u16 * dst16 = ( u16 * ) dst ;
switch ( format ) {
case GE_FORMAT_565 : // BGR 565
2014-05-04 00:18:01 -07:00
if ( UseBGRA8888 ( ) ) {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 19:59:12 +02:00
ConvertBGRA8888ToRGB565 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 00:18:01 -07:00
}
} else {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 19:59:12 +02:00
ConvertRGBA8888ToRGB565 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 00:18:01 -07:00
}
2013-07-05 02:31:31 +01:00
}
break ;
case GE_FORMAT_5551 : // ABGR 1555
2014-05-04 00:18:01 -07:00
if ( UseBGRA8888 ( ) ) {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
ConvertBGRA8888ToRGBA5551 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-06-15 19:33:32 -07:00
}
2014-05-04 00:18:01 -07:00
} else {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
ConvertRGBA8888ToRGBA5551 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-06-15 19:33:32 -07:00
}
2013-07-05 02:31:31 +01:00
}
break ;
case GE_FORMAT_4444 : // ABGR 4444
2014-05-04 00:18:01 -07:00
if ( UseBGRA8888 ( ) ) {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 19:59:12 +02:00
ConvertBGRA8888ToRGBA4444 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 00:18:01 -07:00
}
} else {
2014-06-15 19:33:32 -07:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 19:59:12 +02:00
ConvertRGBA8888ToRGBA4444 ( dst16 , src32 , width ) ;
2014-09-13 16:37:59 -07:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 00:18:01 -07:00
}
2013-07-05 02:31:31 +01:00
}
break ;
2013-07-29 23:05:59 -07:00
case GE_FORMAT_8888 :
2014-05-04 00:18:01 -07:00
case GE_FORMAT_INVALID :
2013-07-29 23:05:59 -07:00
// Not possible.
break ;
2013-06-25 13:50:35 +01:00
}
2013-06-28 14:48:36 +01:00
}
}
2015-12-06 09:08:38 -08:00
# ifdef DEBUG_READ_PIXELS
2014-05-08 10:43:46 +02:00
// TODO: Make more generic.
static void LogReadPixelsError ( GLenum error ) {
switch ( error ) {
2014-05-08 20:30:40 +08:00
case GL_NO_ERROR :
2014-05-08 10:43:46 +02: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 20:30:40 +08:00
case GL_OUT_OF_MEMORY :
ERROR_LOG ( SCEGE , " glReadPixels: GL_OUT_OF_MEMORY " ) ;
break ;
2015-12-06 09:08:38 -08:00
# ifndef USING_GLES2
2014-05-08 20:30:40 +08:00
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 10:43:46 +02:00
break ;
2015-12-06 09:08:38 -08:00
# endif
default :
ERROR_LOG ( SCEGE , " glReadPixels: %08x " , error ) ;
break ;
2014-05-08 10:43:46 +02:00
}
}
2015-12-06 09:08:38 -08:00
# endif
2014-05-08 10:43:46 +02:00
2015-12-06 09:18:37 -08:00
static void SafeGLReadPixels ( GLint x , GLint y , GLsizei w , GLsizei h , GLenum fmt , GLenum type , void * pixels ) {
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
// Some drivers seem to require we specify this. See #8254.
glPixelStorei ( GL_PACK_ROW_LENGTH , w ) ;
}
2015-12-12 21:21:38 -08:00
glReadPixels ( x , y , w , h , fmt , type , pixels ) ;
2015-12-06 09:18:37 -08:00
# ifdef DEBUG_READ_PIXELS
LogReadPixelsError ( glGetError ( ) ) ;
# endif
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
}
}
2013-08-12 02:30:17 +08:00
void FramebufferManager : : PackFramebufferAsync_ ( VirtualFramebuffer * vfb ) {
const int MAX_PBO = 2 ;
2013-06-28 14:48:36 +01:00
GLubyte * packed = 0 ;
2013-07-02 14:08:59 +01:00
bool unbind = false ;
2014-08-30 02:05:51 -07:00
const u8 nextPBO = ( currentPBO_ + 1 ) % MAX_PBO ;
2015-09-05 19:58:47 +02:00
const bool useCPU = gstate_c . Supports ( GPU_PREFER_CPU_DOWNLOAD ) ;
2013-08-12 02:30:17 +08:00
2013-07-04 15:24:37 +01:00
// We'll prepare two PBOs to switch between readying and reading
2013-10-09 11:01:52 +02:00
if ( ! pixelBufObj_ ) {
2015-12-06 09:08:38 -08:00
if ( ! vfb ) {
// This call is just to flush the buffers. We don't have any yet,
// so there's nothing to do.
return ;
}
2013-08-12 02:30:17 +08:00
GLuint pbos [ MAX_PBO ] ;
glGenBuffers ( MAX_PBO , pbos ) ;
pixelBufObj_ = new AsyncPBO [ MAX_PBO ] ;
2013-10-09 11:01:52 +02:00
for ( int i = 0 ; i < MAX_PBO ; i + + ) {
2013-08-12 02:30:17 +08:00
pixelBufObj_ [ i ] . handle = pbos [ i ] ;
pixelBufObj_ [ i ] . maxSize = 0 ;
pixelBufObj_ [ i ] . reading = false ;
}
}
// Receive previously requested data from a PBO
2014-05-04 00:18:01 -07:00
AsyncPBO & pbo = pixelBufObj_ [ nextPBO ] ;
if ( pbo . reading ) {
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo . handle ) ;
2015-12-06 09:08:38 -08:00
# ifdef USING_GLES2
// Not on desktop GL 2.x...
packed = ( GLubyte * ) glMapBufferRange ( GL_PIXEL_PACK_BUFFER , 0 , pbo . size , GL_MAP_READ_BIT ) ;
# else
2013-08-12 02:30:17 +08:00
packed = ( GLubyte * ) glMapBuffer ( GL_PIXEL_PACK_BUFFER , GL_READ_ONLY ) ;
2015-12-06 09:08:38 -08:00
# endif
2013-07-02 14:08:59 +01:00
2013-10-09 11:01:52 +02:00
if ( packed ) {
2013-10-19 14:04:46 -07:00
DEBUG_LOG ( SCEGE , " Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u " ,
2014-05-04 00:18:01 -07:00
pbo . size , packed , pbo . fb_address , pbo . stride , nextPBO ) ;
2013-08-12 02:30:17 +08:00
2014-05-04 00:18:01 -07:00
if ( useCPU | | ( UseBGRA8888 ( ) & & pbo . format = = GE_FORMAT_8888 ) ) {
u8 * dst = Memory : : GetPointer ( pbo . fb_address ) ;
2014-09-13 16:37:59 -07:00
ConvertFromRGBA8888 ( dst , packed , pbo . stride , pbo . stride , pbo . stride , pbo . height , pbo . format ) ;
2013-10-09 11:01:52 +02:00
} else {
2013-09-22 17:03:39 +08:00
// We don't need to convert, GPU already did (or should have)
2015-04-05 18:03:50 -07:00
Memory : : MemcpyUnchecked ( pbo . fb_address , packed , pbo . size ) ;
2013-08-12 02:30:17 +08:00
}
2013-07-02 14:08:59 +01:00
2014-05-04 00:18:01 -07:00
pbo . reading = false ;
2013-08-12 02:30:17 +08:00
}
2013-07-02 14:08:59 +01:00
2013-08-12 02:30:17 +08:00
glUnmapBuffer ( GL_PIXEL_PACK_BUFFER ) ;
unbind = true ;
2013-07-02 14:08:59 +01:00
}
2013-06-28 14:48:36 +01:00
// Order packing/readback of the framebuffer
2013-10-09 11:01:52 +02:00
if ( vfb ) {
2013-06-25 13:50:35 +01:00
int pixelType , pixelSize , pixelFormat , align ;
2015-09-05 19:58:47 +02:00
bool reverseOrder = gstate_c . Supports ( GPU_PREFER_REVERSE_COLOR_ORDER ) ;
2013-06-25 13:50:35 +01:00
switch ( vfb - > format ) {
2013-07-04 15:24:37 +01: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
2015-12-06 09:08:38 -08:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_4_4_4_4 ;
# else
2013-08-19 20:18:32 +02:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4 ) ;
2015-12-06 09:08:38 -08:00
# endif
2013-07-04 15:24:37 +01:00
pixelFormat = GL_RGBA ;
2013-06-25 19:14:10 +01:00
pixelSize = 2 ;
2014-07-04 14:01:56 -07:00
align = 2 ;
2013-06-25 19:14:10 +01:00
break ;
2013-07-04 15:24:37 +01:00
case GE_FORMAT_5551 : // 16 bit RGBA
2015-12-06 09:08:38 -08:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_5_5_5_1 ;
# else
2013-08-19 20:18:32 +02:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1 ) ;
2015-12-06 09:08:38 -08:00
# endif
2013-07-04 15:24:37 +01:00
pixelFormat = GL_RGBA ;
2013-06-25 19:14:10 +01:00
pixelSize = 2 ;
2014-07-04 14:01:56 -07:00
align = 2 ;
2013-06-25 19:14:10 +01:00
break ;
2013-07-04 15:24:37 +01:00
case GE_FORMAT_565 : // 16 bit RGB
2015-12-06 09:08:38 -08:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_5_6_5 ;
# else
2013-08-19 20:18:32 +02:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5 ) ;
2015-12-06 09:08:38 -08:00
# endif
2013-07-04 15:24:37 +01:00
pixelFormat = GL_RGB ;
2013-06-25 19:14:10 +01:00
pixelSize = 2 ;
2014-07-04 14:01:56 -07:00
align = 2 ;
2013-06-25 19:14:10 +01:00
break ;
2013-07-04 15:24:37 +01:00
case GE_FORMAT_8888 : // 32 bit RGBA
2013-06-28 14:48:36 +01:00
default :
pixelType = GL_UNSIGNED_BYTE ;
2014-05-04 00:18:01 -07:00
pixelFormat = UseBGRA8888 ( ) ? GL_BGRA_EXT : GL_RGBA ;
2013-06-25 19:14:10 +01:00
pixelSize = 4 ;
align = 4 ;
break ;
2013-06-25 13:50:35 +01:00
}
2014-05-03 20:20:40 -07:00
// If using the CPU, we need 4 bytes per pixel always.
u32 bufSize = vfb - > fb_stride * vfb - > height * ( useCPU ? 4 : pixelSize ) ;
2013-09-20 01:28:14 +08:00
u32 fb_address = ( 0x04000000 ) | vfb - > fb_address ;
2013-06-28 14:48:36 +01:00
2013-08-12 02:30:17 +08:00
if ( vfb - > fbo ) {
fbo_bind_for_read ( vfb - > fbo ) ;
} else {
2013-12-22 16:03:39 +08:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferAsync_: vfb->fbo == 0 " ) ;
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2013-08-12 02:30:17 +08:00
return ;
}
2014-07-04 01:54:03 -07:00
GLenum fbStatus ;
2015-09-05 22:40:45 +02:00
fbStatus = ( GLenum ) fbo_check_framebuffer_status ( vfb - > fbo ) ;
2014-07-04 01:54:03 -07:00
if ( fbStatus ! = GL_FRAMEBUFFER_COMPLETE ) {
2013-09-07 22:02:55 +02:00
ERROR_LOG ( SCEGE , " Incomplete source framebuffer, aborting read " ) ;
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2013-08-12 02:30:17 +08:00
return ;
2013-06-25 13:50:35 +01:00
}
2013-06-28 14:48:36 +01:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pixelBufObj_ [ currentPBO_ ] . handle ) ;
2013-10-09 11:01:52 +02:00
if ( pixelBufObj_ [ currentPBO_ ] . maxSize < bufSize ) {
2013-07-04 15:24:37 +01:00
// We reserve a buffer big enough to fit all those pixels
2014-05-03 20:20:40 -07:00
glBufferData ( GL_PIXEL_PACK_BUFFER , bufSize , NULL , GL_DYNAMIC_READ ) ;
2013-06-28 14:48:36 +01:00
pixelBufObj_ [ currentPBO_ ] . maxSize = bufSize ;
2013-06-25 13:50:35 +01:00
}
2013-10-09 11:01:52 +02:00
if ( useCPU ) {
2013-07-04 15:24:37 +01:00
// If converting pixel formats on the CPU we'll always request RGBA8888
2013-06-28 14:48:36 +01:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , UseBGRA8888 ( ) ? GL_BGRA_EXT : GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2013-06-28 14:48:36 +01:00
} else {
2013-07-04 15:24:37 +01:00
// Otherwise we'll directly request the format we need and let the GPU sort it out
2013-06-28 14:48:36 +01:00
glPixelStorei ( GL_PACK_ALIGNMENT , align ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , pixelFormat , pixelType , 0 ) ;
2013-06-28 14:48:36 +01:00
}
2013-07-02 14:08:59 +01:00
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2013-07-02 14:08:59 +01:00
unbind = true ;
2013-06-28 14:48:36 +01:00
2013-08-12 02:30:17 +08: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 14:48:36 +01:00
}
currentPBO_ = nextPBO ;
2013-10-09 11:01:52 +02:00
if ( unbind ) {
2013-07-02 14:08:59 +01:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
2013-06-28 14:48:36 +01:00
}
}
2014-05-25 19:48:46 -07:00
void FramebufferManager : : PackFramebufferSync_ ( VirtualFramebuffer * vfb , int x , int y , int w , int h ) {
2013-09-22 17:03:39 +08:00
if ( vfb - > fbo ) {
2013-07-02 14:08:59 +01:00
fbo_bind_for_read ( vfb - > fbo ) ;
} else {
2013-09-22 17:03:39 +08:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferSync_: vfb->fbo == 0 " ) ;
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2013-07-02 14:08:59 +01:00
return ;
}
2013-07-04 15:24:37 +01:00
// Pixel size always 4 here because we always request RGBA8888
2014-06-28 02:35:10 -07:00
size_t bufSize = vfb - > fb_stride * std : : max ( vfb - > height , ( u16 ) h ) * 4 ;
2013-08-12 02:30:17 +08:00
u32 fb_address = ( 0x04000000 ) | vfb - > fb_address ;
2013-06-28 14:48:36 +01:00
2013-07-02 14:08:59 +01:00
GLubyte * packed = 0 ;
2014-05-09 21:01:17 +02:00
bool convert = vfb - > format ! = GE_FORMAT_8888 | | UseBGRA8888 ( ) ;
2014-06-15 19:33:32 -07:00
const int dstBpp = vfb - > format = = GE_FORMAT_8888 ? 4 : 2 ;
2014-05-09 21:01:17 +02:00
2014-05-09 21:26:46 +02:00
if ( ! convert ) {
2013-07-02 14:08:59 +01:00
packed = ( GLubyte * ) Memory : : GetPointer ( fb_address ) ;
2013-07-04 15:24:37 +01: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-25 19:48:46 -07:00
u32 neededSize = ( u32 ) bufSize * sizeof ( GLubyte ) ;
if ( ! convBuf_ | | convBufSize_ < neededSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ neededSize ] ;
convBufSize_ = neededSize ;
}
packed = convBuf_ ;
2013-06-28 14:48:36 +01:00
}
2013-10-09 11:01:52 +02:00
if ( packed ) {
2013-09-07 22:02:55 +02:00
DEBUG_LOG ( SCEGE , " Reading framebuffer to mem, bufSize = %u, packed = %p, fb_address = %08x " ,
2013-07-06 19:08:59 +02:00
( u32 ) bufSize , packed , fb_address ) ;
2013-06-28 14:48:36 +01:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2014-05-04 08:43:30 -07:00
GLenum glfmt = GL_RGBA ;
2014-05-09 21:01:17 +02:00
if ( UseBGRA8888 ( ) ) {
2014-05-04 08:43:30 -07:00
glfmt = GL_BGRA_EXT ;
2014-05-09 21:01:17 +02:00
}
2014-06-02 12:08:49 +10:00
2014-05-25 19:48:46 -07:00
int byteOffset = y * vfb - > fb_stride * 4 ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , y , vfb - > fb_stride , h , glfmt , GL_UNSIGNED_BYTE , packed + byteOffset ) ;
2013-06-28 14:48:36 +01:00
2014-05-09 21:01:17 +02:00
if ( convert ) {
2014-06-15 19:33:32 -07:00
int dstByteOffset = y * vfb - > fb_stride * dstBpp ;
2014-09-13 16:37:59 -07:00
ConvertFromRGBA8888 ( Memory : : GetPointer ( fb_address + dstByteOffset ) , packed + byteOffset , vfb - > fb_stride , vfb - > fb_stride , vfb - > width , h , vfb - > format ) ;
2013-06-28 14:48:36 +01:00
}
}
2015-03-14 16:11:20 -07:00
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
# ifdef USING_GLES2
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
fbo_bind_as_render_target ( vfb - > fbo ) ;
const GLenum target = GL_FRAMEBUFFER ;
# else
const GLenum target = GL_READ_FRAMEBUFFER ;
# endif
GLenum attachments [ 3 ] = { GL_COLOR_ATTACHMENT0 , GL_DEPTH_ATTACHMENT , GL_STENCIL_ATTACHMENT } ;
glInvalidateFramebuffer ( target , 3 , attachments ) ;
}
2014-08-27 23:07:11 -07:00
fbo_unbind_read ( ) ;
2013-06-25 13:50:35 +01:00
}
2015-09-23 12:25:38 +02:00
# ifdef _WIN32
void ShowScreenResolution ( ) ;
# endif
2013-03-03 13:00:21 +01:00
void FramebufferManager : : EndFrame ( ) {
2013-01-30 21:09:53 +01:00
if ( resized_ ) {
2015-09-27 19:59:47 +02:00
// TODO: Only do this if the new size actually changed the renderwidth/height.
2013-01-30 21:09:53 +01:00
DestroyAllFBOs ( ) ;
2015-09-29 22:10:01 +02:00
2015-09-27 19:59:47 +02:00
// Probably not necessary
glstate . viewport . set ( 0 , 0 , PSP_CoreParameter ( ) . pixelWidth , PSP_CoreParameter ( ) . pixelHeight ) ;
2015-09-23 12:25:38 +02:00
2015-10-14 18:32:13 +02:00
// Check if postprocessing shader is doing upscaling as it requires native resolution
const ShaderInfo * shaderInfo = 0 ;
if ( g_Config . sPostShaderName ! = " Off " ) {
shaderInfo = GetPostShaderInfo ( g_Config . sPostShaderName ) ;
}
2015-11-14 11:00:10 -08:00
postShaderIsUpscalingFilter_ = shaderInfo ? shaderInfo - > isUpscalingFilter : false ;
2015-09-23 12:25:38 +02:00
// Actually, auto mode should be more granular...
// Round up to a zoom factor for the render size.
2015-09-19 23:42:22 +02:00
int zoom = g_Config . iInternalResolution ;
2015-09-23 12:25:38 +02:00
if ( zoom = = 0 ) { // auto mode
// Use the longest dimension
2015-09-25 19:08:48 +02:00
if ( ! g_Config . IsPortrait ( ) ) {
zoom = ( PSP_CoreParameter ( ) . pixelWidth + 479 ) / 480 ;
2015-09-23 12:25:38 +02:00
} else {
2015-09-25 19:08:48 +02:00
zoom = ( PSP_CoreParameter ( ) . pixelHeight + 479 ) / 480 ;
2015-09-23 12:25:38 +02:00
}
}
2015-10-14 18:32:13 +02:00
if ( zoom < = 1 | | postShaderIsUpscalingFilter_ )
2015-09-23 12:25:38 +02:00
zoom = 1 ;
if ( g_Config . IsPortrait ( ) ) {
PSP_CoreParameter ( ) . renderWidth = 272 * zoom ;
PSP_CoreParameter ( ) . renderHeight = 480 * zoom ;
} else {
PSP_CoreParameter ( ) . renderWidth = 480 * zoom ;
PSP_CoreParameter ( ) . renderHeight = 272 * zoom ;
}
2015-09-27 19:59:47 +02:00
UpdateSize ( ) ;
2013-01-30 21:09:53 +01:00
resized_ = false ;
2015-09-23 12:25:38 +02:00
# ifdef _WIN32
2015-11-27 00:16:03 +01:00
// Seems related - if you're ok with numbers all the time, show some more :)
if ( g_Config . iShowFPSCounter ! = 0 ) {
ShowScreenResolution ( ) ;
}
2015-09-23 12:25:38 +02:00
# endif
2015-09-29 22:10:01 +02:00
ClearBuffer ( ) ;
DestroyDraw2DProgram ( ) ;
SetLineWidth ( ) ;
2013-01-30 21:09:53 +01:00
}
2013-07-02 14:10:20 +01:00
2014-05-08 14:26:17 +02:00
// We flush to memory last requested framebuffer, if any.
// Only do this in the read-framebuffer modes.
2014-05-26 23:26:29 -07:00
if ( updateVRAM_ )
2015-12-06 09:08:38 -08:00
PackFramebufferAsync_ ( nullptr ) ;
2015-03-14 16:02:04 -07:00
// Let's explicitly invalidate any temp FBOs used during this frame.
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
for ( auto temp : tempFBOs_ ) {
if ( temp . second . last_frame_used < gpuStats . numFlips ) {
continue ;
}
fbo_bind_as_render_target ( temp . second . fbo ) ;
GLenum attachments [ 3 ] = { GL_COLOR_ATTACHMENT0 , GL_STENCIL_ATTACHMENT , GL_DEPTH_ATTACHMENT } ;
glInvalidateFramebuffer ( GL_FRAMEBUFFER , 3 , attachments ) ;
}
fbo_unbind ( ) ;
}
2013-01-30 21:09:53 +01:00
}
2013-06-11 11:28:41 +02:00
void FramebufferManager : : DeviceLost ( ) {
DestroyAllFBOs ( ) ;
2013-09-26 12:41:07 +02:00
DestroyDraw2DProgram ( ) ;
2013-06-11 11:28:41 +02:00
resized_ = false ;
}
2013-03-03 13:00:21 +01:00
std : : vector < FramebufferInfo > FramebufferManager : : GetFramebufferList ( ) {
2013-02-17 01:06:06 +01:00
std : : vector < FramebufferInfo > list ;
2013-06-23 08:51:35 -07:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-02-17 01:06:06 +01: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 07:00:59 -07:00
void FramebufferManager : : DecimateFBOs ( ) {
2013-03-11 22:22:22 +01:00
fbo_unbind ( ) ;
2013-06-09 02:11:16 -07:00
currentRenderVfb_ = 0 ;
2013-09-22 17:03:39 +08:00
2013-06-23 08:51:35 -07:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2014-09-09 08:12:42 -07:00
int age = frameLastFramebufUsed_ - std : : max ( vfb - > last_frame_render , vfb - > last_frame_used ) ;
2013-07-02 14:10:20 +01:00
2014-05-26 11:40:46 -07:00
if ( ShouldDownloadFramebuffer ( vfb ) & & age = = 0 & & ! vfb - > memoryUpdated ) {
2015-12-06 09:08:38 -08:00
bool sync = gl_extensions . IsGLES ;
2014-05-08 16:09:55 +02:00
ReadFramebufferToMemory ( vfb , sync , 0 , 0 , vfb - > width , vfb - > height ) ;
}
2013-09-29 10:30:25 +08:00
2015-03-23 20:29:51 +08:00
// Let's also "decimate" the usageFlags.
UpdateFramebufUsage ( vfb ) ;
2015-03-14 14:58:32 -07:00
if ( vfb ! = displayFramebuf_ & & vfb ! = prevDisplayFramebuf_ & & vfb ! = prevPrevDisplayFramebuf_ ) {
if ( age > FBO_OLD_AGE ) {
INFO_LOG ( SCEGE , " Decimating FBO for %08x (%i x %i x %i), age %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format , age ) ;
DestroyFramebuf ( vfb ) ;
vfbs_ . erase ( vfbs_ . begin ( ) + i - - ) ;
}
2013-01-30 21:09:53 +01:00
}
}
2013-06-28 14:48:36 +01:00
2014-05-31 22:42:09 -07:00
for ( auto it = tempFBOs_ . begin ( ) ; it ! = tempFBOs_ . end ( ) ; ) {
2014-09-09 08:12:42 -07:00
int age = frameLastFramebufUsed_ - it - > second . last_frame_used ;
2014-05-31 22:42:09 -07:00
if ( age > FBO_OLD_AGE ) {
fbo_destroy ( it - > second . fbo ) ;
tempFBOs_ . erase ( it + + ) ;
} else {
+ + it ;
}
}
2013-06-28 14:48:36 +01:00
// Do the same for ReadFramebuffersToMemory's VFBs
for ( size_t i = 0 ; i < bvfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = bvfbs_ [ i ] ;
2014-09-09 08:12:42 -07:00
int age = frameLastFramebufUsed_ - vfb - > last_frame_render ;
2013-06-28 14:48:36 +01:00
if ( age > FBO_OLD_AGE ) {
2014-06-29 18:55:23 -07: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 14:48:36 +01:00
DestroyFramebuf ( vfb ) ;
bvfbs_ . erase ( bvfbs_ . begin ( ) + i - - ) ;
}
}
2013-01-30 21:09:53 +01:00
}
void FramebufferManager : : DestroyAllFBOs ( ) {
2013-03-11 22:22:22 +01:00
fbo_unbind ( ) ;
2013-06-09 02:11:16 -07:00
currentRenderVfb_ = 0 ;
2013-06-11 04:07:48 +08:00
displayFramebuf_ = 0 ;
prevDisplayFramebuf_ = 0 ;
prevPrevDisplayFramebuf_ = 0 ;
2013-06-09 02:11:16 -07:00
2013-06-23 08:51:35 -07:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-09-07 22:02:55 +02: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 08:16:22 -07:00
DestroyFramebuf ( vfb ) ;
2013-01-30 21:09:53 +01:00
}
vfbs_ . clear ( ) ;
2014-07-04 23:27:12 -07:00
for ( size_t i = 0 ; i < bvfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = bvfbs_ [ i ] ;
DestroyFramebuf ( vfb ) ;
}
bvfbs_ . clear ( ) ;
for ( auto it = tempFBOs_ . begin ( ) , end = tempFBOs_ . end ( ) ; it ! = end ; + + it ) {
fbo_destroy ( it - > second . fbo ) ;
}
tempFBOs_ . clear ( ) ;
2014-08-03 18:45:36 -07:00
fbo_unbind ( ) ;
DisableState ( ) ;
2013-01-30 21:09:53 +01:00
}
2013-01-31 19:14:57 +08:00
2014-06-11 00:28:28 -07:00
void FramebufferManager : : FlushBeforeCopy ( ) {
// Flush anything not yet drawn before blitting, downloading, or uploading.
// This might be a stalled list, or unflushed before a block transfer, etc.
2015-08-05 02:43:40 +02:00
// TODO: It's really bad that we are calling SetRenderFramebuffer here with
// all the irrelevant state checking it'll use to decide what to do. Should
// do something more focused here.
2015-07-26 22:38:40 +02:00
SetRenderFrameBuffer ( gstate_c . framebufChanged , gstate_c . skipDrawReason ) ;
2014-06-11 00:28:28 -07:00
transformDraw_ - > Flush ( ) ;
}
2013-01-31 19:14:57 +08:00
void FramebufferManager : : Resized ( ) {
resized_ = true ;
}
2013-09-22 19:03:31 -07:00
2015-08-05 12:13:14 +02:00
bool FramebufferManager : : GetFramebuffer ( u32 fb_address , int fb_stride , GEBufferFormat format , GPUDebugBuffer & buffer ) {
2013-09-22 19:03:31 -07: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?
2015-08-05 02:43:40 +02:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , format ) ;
2013-09-22 19:03:31 -07:00
return true ;
}
2015-11-01 23:09:41 +01:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GE_FORMAT_8888 , false , true ) ;
2013-09-29 13:03:36 +08:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2015-09-05 23:39:10 +02:00
if ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES )
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
2013-09-22 19:03:31 -07:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_RGBA , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2013-09-22 19:03:31 -07:00
return true ;
}
2013-09-28 02:14:27 -07:00
2014-12-20 08:31:56 -08:00
bool FramebufferManager : : GetDisplayFramebuffer ( GPUDebugBuffer & buffer ) {
fbo_unbind_read ( ) ;
2015-09-19 16:19:03 +02:00
int pw = PSP_CoreParameter ( ) . pixelWidth ;
int ph = PSP_CoreParameter ( ) . pixelHeight ;
2015-11-02 19:43:36 -08:00
// The backbuffer is flipped.
buffer . Allocate ( pw , ph , GPU_DBG_FORMAT_888_RGB , true ) ;
2014-12-20 08:31:56 -08:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , pw , ph , GL_RGB , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2014-12-20 08:31:56 -08:00
return true ;
}
2015-08-05 12:13:14 +02:00
bool FramebufferManager : : GetDepthbuffer ( u32 fb_address , int fb_stride , u32 z_address , int z_stride , GPUDebugBuffer & buffer ) {
2013-09-28 02:14:27 -07: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-05 17:19:17 -07:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( z_address | 0x04000000 ) , z_stride , 512 , GPU_DBG_FORMAT_16BIT ) ;
2013-09-28 02:14:27 -07:00
return true ;
}
2015-11-01 23:09:41 +01:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_FLOAT , false ) ;
2013-09-29 13:03:36 +08:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2015-09-05 23:39:10 +02:00
if ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES )
glReadBuffer ( GL_DEPTH_ATTACHMENT ) ;
2013-09-28 02:14:27 -07:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_DEPTH_COMPONENT , GL_FLOAT , buffer . GetData ( ) ) ;
2013-09-28 02:14:27 -07:00
return true ;
}
2015-08-05 12:13:14 +02:00
bool FramebufferManager : : GetStencilbuffer ( u32 fb_address , int fb_stride , GPUDebugBuffer & buffer ) {
2013-09-28 02:14:27 -07: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-05 17:19:17 -07:00
// TODO: Actually get the stencil.
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , GPU_DBG_FORMAT_8888 ) ;
2013-09-28 02:14:27 -07:00
return true ;
}
# ifndef USING_GLES2
2015-11-01 23:09:41 +01:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_8BIT , false ) ;
2013-09-29 13:03:36 +08:00
if ( vfb - > fbo )
fbo_bind_for_read ( vfb - > fbo ) ;
2013-10-05 17:19:17 -07:00
glReadBuffer ( GL_STENCIL_ATTACHMENT ) ;
2013-09-28 02:14:27 -07:00
glPixelStorei ( GL_PACK_ALIGNMENT , 2 ) ;
2015-12-06 09:18:37 -08:00
SafeGLReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_STENCIL_INDEX , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2013-09-28 02:14:27 -07:00
return true ;
# else
return false ;
# endif
}