2016-08-08 00:59:35 +00:00
// Copyright (c) 2012- PPSSPP Project.
2012-11-01 15:19:01 +00: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 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-12-29 22:44:35 +00:00
# include <set>
# include <algorithm>
2015-07-11 22:44:11 +00:00
# include "profiler/profiler.h"
2017-02-24 19:50:27 +00:00
# include "gfx/gl_common.h"
2017-03-03 13:15:27 +00:00
# include "gfx/gl_debug_log.h"
2012-11-01 15:19:01 +00:00
# include "gfx_es2/glsl_program.h"
2017-02-06 10:55:54 +00:00
# include "thin3d/thin3d.h"
2013-01-30 20:09:53 +00:00
2013-12-02 16:24:20 +00:00
# include "base/timeutil.h"
2012-11-01 15:19:01 +00:00
# include "math/lin/matrix4x4.h"
2015-04-07 22:16:22 +00:00
# include "Common/ColorConv.h"
2013-01-30 20:09:53 +00:00
# include "Core/Host.h"
# include "Core/MemMap.h"
# include "Core/Config.h"
# include "Core/System.h"
2013-09-11 20:21:15 +00:00
# include "Core/Reporting.h"
2013-01-30 20:09:53 +00:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2012-11-01 15:19:01 +00:00
2013-10-12 00:05:55 +00:00
# include "GPU/Common/PostShader.h"
2014-05-04 07:18:01 +00:00
# include "GPU/Common/TextureDecoder.h"
2015-01-23 09:40:49 +00:00
# include "GPU/Common/FramebufferCommon.h"
2014-06-13 15:35:12 +00:00
# include "GPU/Debugger/Stepping.h"
2017-02-04 17:46:12 +00:00
# include "ext/native/gfx/GLStateCache.h"
2017-01-21 21:16:30 +00:00
# include "GPU/GLES/FramebufferManagerGLES.h"
# include "GPU/GLES/TextureCacheGLES.h"
2016-04-10 08:27:28 +00:00
# include "GPU/GLES/DrawEngineGLES.h"
2017-01-21 21:16:30 +00:00
# include "GPU/GLES/ShaderManagerGLES.h"
2012-11-01 15:19:01 +00:00
2015-12-06 17:08:38 +00:00
// #define DEBUG_READ_PIXELS 1
2013-01-30 20:09:53 +00:00
static const char tex_fs [ ] =
2013-10-20 22:36:23 +00:00
# ifdef USING_GLES2
2012-11-01 15:19:01 +00:00
" precision mediump float; \n "
2013-10-20 22:36:23 +00:00
# endif
2012-11-01 15:19:01 +00:00
" uniform sampler2D sampler0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2014-06-18 06:10:38 +00:00
" gl_FragColor = texture2D(sampler0, v_texcoord0); \n "
2012-11-01 15:19:01 +00:00
" } \n " ;
2013-01-30 20:09:53 +00:00
static const char basic_vs [ ] =
2012-11-01 15:19:01 +00:00
" attribute vec4 a_position; \n "
" attribute vec2 a_texcoord0; \n "
" varying vec2 v_texcoord0; \n "
" void main() { \n "
2012-12-21 10:24:38 +00:00
" v_texcoord0 = a_texcoord0; \n "
2013-10-30 13:37:07 +00:00
" gl_Position = a_position; \n "
2012-11-01 15:19:01 +00:00
" } \n " ;
2014-09-13 23:37:59 +00:00
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 dstStride , u32 srcStride , u32 width , u32 height , GEBufferFormat format ) ;
2013-06-28 13:48:36 +00:00
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : DisableState ( ) {
2013-08-14 19:32:43 +00:00
glstate . blend . disable ( ) ;
glstate . cullFace . disable ( ) ;
glstate . depthTest . disable ( ) ;
glstate . scissorTest . disable ( ) ;
glstate . stencilTest . disable ( ) ;
2013-08-23 09:32:10 +00:00
# if !defined(USING_GLES2)
glstate . colorLogicOp . disable ( ) ;
# endif
2013-12-04 13:02:40 +00:00
glstate . colorMask . set ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE ) ;
2014-08-04 04:42:21 +00:00
glstate . stencilMask . set ( 0xFF ) ;
2013-08-14 19:32:43 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : CompileDraw2DProgram ( ) {
2013-09-26 10:41:07 +00:00
if ( ! draw2dprogram_ ) {
2013-10-13 10:05:50 +00:00
std : : string errorString ;
draw2dprogram_ = glsl_create_source ( basic_vs , tex_fs , & errorString ) ;
if ( ! draw2dprogram_ ) {
ERROR_LOG_REPORT ( G3D , " Failed to compile draw2dprogram! This shouldn't happen. \n %s " , errorString . c_str ( ) ) ;
} else {
glsl_bind ( draw2dprogram_ ) ;
glUniform1i ( draw2dprogram_ - > sampler0 , 0 ) ;
}
2017-02-23 11:41:22 +00:00
CompilePostShader ( ) ;
}
}
2013-09-26 10:41:07 +00:00
2017-02-23 11:41:22 +00:00
void FramebufferManagerGLES : : CompilePostShader ( ) {
SetNumExtraFBOs ( 0 ) ;
const ShaderInfo * shaderInfo = 0 ;
if ( g_Config . sPostShaderName ! = " Off " ) {
2017-03-11 11:25:43 +00:00
ReloadAllPostShaderInfo ( ) ;
2017-02-23 11:41:22 +00:00
shaderInfo = GetPostShaderInfo ( g_Config . sPostShaderName ) ;
}
2013-10-12 00:05:55 +00:00
2017-02-23 11:41:22 +00:00
if ( shaderInfo ) {
std : : string errorString ;
postShaderAtOutputResolution_ = shaderInfo - > outputResolution ;
postShaderProgram_ = glsl_create ( shaderInfo - > vertexShaderFile . c_str ( ) , shaderInfo - > fragmentShaderFile . c_str ( ) , & errorString ) ;
if ( ! postShaderProgram_ ) {
// DO NOT turn this into a report, as it will pollute our logs with all kinds of
// user shader experiments.
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " Failed to build post-processing program from %s and %s! \n %s " , shaderInfo - > vertexShaderFile . c_str ( ) , shaderInfo - > fragmentShaderFile . c_str ( ) , errorString . c_str ( ) ) ;
2017-02-23 11:41:22 +00:00
// let's show the first line of the error string as an OSM.
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 ;
for ( size_t i = 0 ; i < errorString . size ( ) ; i + + ) {
if ( errorString [ i ] = = ' \n ' ) {
firstLine = errorString . substr ( start , i - start ) ;
if ( blacklistedLines . find ( firstLine ) = = blacklistedLines . end ( ) ) {
break ;
2013-10-13 10:05:50 +00:00
}
2017-02-23 11:41:22 +00:00
start = i + 1 ;
firstLine . clear ( ) ;
2013-10-13 10:05:50 +00:00
}
2017-02-23 11:41:22 +00:00
}
if ( ! firstLine . empty ( ) ) {
host - > NotifyUserMessage ( " Post-shader error: " + firstLine + " ... " , 10.0f , 0xFF3090FF ) ;
2013-10-09 14:08:36 +00:00
} else {
2017-02-23 11:41:22 +00:00
host - > NotifyUserMessage ( " Post-shader error, see log for details " , 10.0f , 0xFF3090FF ) ;
2013-10-09 14:08:36 +00:00
}
2013-10-12 00:05:55 +00:00
usePostShader_ = false ;
2017-02-23 11:41:22 +00:00
} else {
glsl_bind ( postShaderProgram_ ) ;
glUniform1i ( postShaderProgram_ - > sampler0 , 0 ) ;
SetNumExtraFBOs ( 1 ) ;
deltaLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_texelDelta " ) ;
pixelDeltaLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_pixelDelta " ) ;
timeLoc_ = glsl_uniform_loc ( postShaderProgram_ , " u_time " ) ;
usePostShader_ = true ;
2013-09-26 10:41:07 +00:00
}
2017-02-23 11:41:22 +00:00
} else {
postShaderProgram_ = nullptr ;
usePostShader_ = false ;
2013-07-16 20:50:53 +00:00
}
2017-02-23 11:41:22 +00:00
glsl_unbind ( ) ;
2013-07-16 20:50:53 +00:00
}
2017-02-15 15:01:59 +00:00
void FramebufferManagerGLES : : Bind2DShader ( ) {
glsl_bind ( draw2dprogram_ ) ;
}
2017-02-15 11:18:08 +00:00
void FramebufferManagerGLES : : BindPostShader ( const PostShaderUniforms & uniforms ) {
2017-02-15 22:11:46 +00:00
// Make sure we've compiled the shader.
if ( ! postShaderProgram_ ) {
CompileDraw2DProgram ( ) ;
}
2017-02-15 11:18:08 +00:00
glsl_bind ( postShaderProgram_ ) ;
2015-10-31 15:02:25 +00:00
if ( deltaLoc_ ! = - 1 )
2017-02-15 11:09:51 +00:00
glUniform2f ( deltaLoc_ , uniforms . texelDelta [ 0 ] , uniforms . texelDelta [ 1 ] ) ;
2015-10-31 15:02:25 +00:00
if ( pixelDeltaLoc_ ! = - 1 )
2017-02-15 11:09:51 +00:00
glUniform2f ( pixelDeltaLoc_ , uniforms . pixelDelta [ 0 ] , uniforms . pixelDelta [ 1 ] ) ;
2015-10-31 15:02:25 +00:00
if ( timeLoc_ ! = - 1 ) {
2017-02-15 11:09:51 +00:00
glUniform4fv ( timeLoc_ , 1 , uniforms . time ) ;
2015-10-31 15:02:25 +00:00
}
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : DestroyDraw2DProgram ( ) {
2013-09-26 10:41:07 +00:00
if ( draw2dprogram_ ) {
glsl_destroy ( draw2dprogram_ ) ;
2015-08-05 10:13:14 +00:00
draw2dprogram_ = nullptr ;
2013-10-09 14:08:36 +00:00
}
2013-10-12 00:05:55 +00:00
if ( postShaderProgram_ ) {
glsl_destroy ( postShaderProgram_ ) ;
2015-08-05 10:13:14 +00:00
postShaderProgram_ = nullptr ;
2013-09-26 10:41:07 +00:00
}
}
2017-02-05 18:51:50 +00:00
FramebufferManagerGLES : : FramebufferManagerGLES ( Draw : : DrawContext * draw ) :
FramebufferManagerCommon ( draw ) ,
2013-06-05 21:03:23 +00:00
drawPixelsTex_ ( 0 ) ,
2013-07-30 06:05:59 +00:00
drawPixelsTexFormat_ ( GE_FORMAT_INVALID ) ,
2015-08-05 10:13:14 +00:00
convBuf_ ( nullptr ) ,
draw2dprogram_ ( nullptr ) ,
postShaderProgram_ ( nullptr ) ,
stencilUploadProgram_ ( nullptr ) ,
2013-12-02 16:24:20 +00:00
timeLoc_ ( - 1 ) ,
2015-10-31 15:02:25 +00:00
pixelDeltaLoc_ ( - 1 ) ,
2016-08-05 17:46:11 +00:00
deltaLoc_ ( - 1 ) ,
2017-02-06 11:02:30 +00:00
textureCacheGL_ ( nullptr ) ,
2017-02-15 17:32:44 +00:00
shaderManagerGL_ ( nullptr ) ,
2015-08-05 10:13:14 +00:00
pixelBufObj_ ( nullptr ) ,
2013-06-28 13:48:36 +00:00
currentPBO_ ( 0 )
2013-01-30 20:09:53 +00:00
{
2017-02-15 15:01:59 +00:00
needBackBufferYSwap_ = true ;
2017-05-23 09:12:10 +00:00
needGLESRebinds_ = true ;
2014-06-15 22:19:49 +00:00
}
2013-01-14 18:26:10 +00:00
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : Init ( ) {
2014-09-13 23:47:23 +00:00
FramebufferManagerCommon : : Init ( ) ;
2015-10-14 18:07:06 +00:00
// Workaround for upscaling shaders where we force x1 resolution without saving it
2017-04-24 18:58:16 +00:00
Resized ( ) ;
2014-06-15 22:19:49 +00:00
CompileDraw2DProgram ( ) ;
2013-10-09 17:00:35 +00:00
SetLineWidth ( ) ;
2012-11-01 15:19:01 +00:00
}
2017-02-06 11:02:30 +00:00
void FramebufferManagerGLES : : SetTextureCache ( TextureCacheGLES * tc ) {
textureCacheGL_ = tc ;
textureCache_ = tc ;
}
2017-02-15 17:32:44 +00:00
void FramebufferManagerGLES : : SetShaderManager ( ShaderManagerGLES * sm ) {
shaderManagerGL_ = sm ;
shaderManager_ = sm ;
}
2017-01-21 21:16:30 +00:00
FramebufferManagerGLES : : ~ FramebufferManagerGLES ( ) {
2013-06-05 21:03:23 +00:00
if ( drawPixelsTex_ )
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
2015-01-25 04:36:43 +00:00
DestroyDraw2DProgram ( ) ;
2014-06-01 01:24:35 +00:00
if ( stencilUploadProgram_ ) {
glsl_destroy ( stencilUploadProgram_ ) ;
}
2013-09-28 20:42:13 +00:00
SetNumExtraFBOs ( 0 ) ;
2013-06-28 13:48:36 +00:00
2014-06-01 05:41:41 +00:00
for ( auto it = tempFBOs_ . begin ( ) , end = tempFBOs_ . end ( ) ; it ! = end ; + + it ) {
2017-02-04 17:46:12 +00:00
delete it - > second . fbo ;
2014-01-20 10:12:44 +00:00
}
2014-01-20 02:27:52 +00:00
2013-06-28 13:48:36 +00:00
delete [ ] pixelBufObj_ ;
2014-05-25 23:28:13 +00:00
delete [ ] convBuf_ ;
2012-11-01 15:19:01 +00:00
}
2017-03-23 03:56:26 +00:00
void FramebufferManagerGLES : : MakePixelTexture ( const u8 * srcPixels , GEBufferFormat srcPixelFormat , int srcStride , int width , int height , float & u1 , float & v1 ) {
2017-03-23 03:57:45 +00:00
// Optimization: skip a copy if possible in a common case.
int texWidth = width ;
if ( srcPixelFormat = = GE_FORMAT_8888 & & width < srcStride ) {
// Don't up the upload requirements too much if subimages are unsupported.
if ( gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) | | width > = 480 ) {
texWidth = srcStride ;
u1 * = ( float ) width / texWidth ;
}
}
if ( drawPixelsTex_ & & ( drawPixelsTexFormat_ ! = srcPixelFormat | | drawPixelsTexW_ ! = texWidth | | drawPixelsTexH_ ! = height ) ) {
2013-06-05 21:03:23 +00:00
glDeleteTextures ( 1 , & drawPixelsTex_ ) ;
drawPixelsTex_ = 0 ;
}
if ( ! drawPixelsTex_ ) {
2017-02-06 11:02:30 +00:00
drawPixelsTex_ = textureCacheGL_ - > AllocTextureName ( ) ;
2017-03-23 03:57:45 +00:00
drawPixelsTexW_ = texWidth ;
2014-05-09 20:26:42 +00:00
drawPixelsTexH_ = height ;
2013-06-05 21:03:23 +00:00
// Initialize backbuffer texture for DrawPixels
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
2014-06-16 02:42:24 +00: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 21:03:23 +00:00
2017-03-23 03:57:45 +00:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , texWidth , height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2014-05-09 20:26:42 +00:00
drawPixelsTexFormat_ = srcPixelFormat ;
2014-05-09 21:11:04 +00:00
} else {
glBindTexture ( GL_TEXTURE_2D , drawPixelsTex_ ) ;
2013-06-05 21:03:23 +00:00
}
2013-06-05 21:26:51 +00:00
// TODO: We can just change the texture format and flip some bits around instead of this.
2014-05-09 21:11:04 +00:00
// Could share code with the texture cache perhaps.
2013-10-21 02:55:01 +00:00
bool useConvBuf = false ;
2017-03-23 03:57:45 +00:00
if ( srcPixelFormat ! = GE_FORMAT_8888 | | srcStride ! = texWidth ) {
2013-10-21 02:55:01 +00:00
useConvBuf = true ;
2017-03-23 03:57:45 +00:00
u32 neededSize = texWidth * height * 4 ;
2014-05-25 23:28:13 +00:00
if ( ! convBuf_ | | convBufSize_ < neededSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ neededSize ] ;
convBufSize_ = neededSize ;
2013-06-05 21:26:51 +00:00
}
2014-05-09 21:11:04 +00:00
for ( int y = 0 ; y < height ; y + + ) {
2014-05-09 20:26:42 +00:00
switch ( srcPixelFormat ) {
2013-07-30 05:58:37 +00:00
case GE_FORMAT_565 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2017-03-23 03:57:45 +00:00
u8 * dst = convBuf_ + 4 * texWidth * y ;
2015-04-08 18:21:48 +00:00
ConvertRGBA565ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_5551 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2017-03-23 03:57:45 +00:00
u8 * dst = convBuf_ + 4 * texWidth * y ;
2015-04-08 18:21:48 +00:00
ConvertRGBA5551ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_4444 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u16 * src = ( const u16 * ) srcPixels + srcStride * y ;
2017-03-23 03:57:45 +00:00
u8 * dst = convBuf_ + 4 * texWidth * y ;
2015-04-08 18:21:48 +00:00
ConvertRGBA4444ToRGBA8888 ( ( u32 * ) dst , src , width ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2012-11-01 15:19:01 +00:00
2013-07-30 05:58:37 +00:00
case GE_FORMAT_8888 :
2012-11-01 15:19:01 +00:00
{
2014-05-09 20:26:42 +00:00
const u8 * src = srcPixels + srcStride * 4 * y ;
2017-03-23 03:57:45 +00:00
u8 * dst = convBuf_ + 4 * texWidth * y ;
2014-05-09 21:11:04 +00:00
memcpy ( dst , src , 4 * width ) ;
2012-11-01 15:19:01 +00:00
}
2013-06-05 21:26:51 +00:00
break ;
2013-08-13 06:40:22 +00:00
case GE_FORMAT_INVALID :
_dbg_assert_msg_ ( G3D , false , " Invalid pixelFormat passed to DrawPixels(). " ) ;
break ;
2012-11-01 15:19:01 +00:00
}
}
}
2017-03-23 03:57:45 +00:00
// Try to skip uploading the unnecessary parts.
if ( gstate_c . Supports ( GPU_SUPPORTS_UNPACK_SUBIMAGE ) & & width ! = texWidth ) {
glPixelStorei ( GL_UNPACK_ROW_LENGTH , texWidth ) ;
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , width , height , GL_RGBA , GL_UNSIGNED_BYTE , useConvBuf ? convBuf_ : srcPixels ) ;
glPixelStorei ( GL_UNPACK_ROW_LENGTH , 0 ) ;
} else {
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , texWidth , height , GL_RGBA , GL_UNSIGNED_BYTE , useConvBuf ? convBuf_ : srcPixels ) ;
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2014-05-09 20:26:42 +00:00
}
2017-02-15 12:05:10 +00:00
void FramebufferManagerGLES : : SetViewport2D ( int x , int y , int w , int h ) {
glstate . viewport . set ( x , y , w , h ) ;
}
2014-05-08 13:16:33 +00:00
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
2017-02-15 11:32:48 +00:00
// TODO: This could totally use fbo_blit in many cases.
void FramebufferManagerGLES : : DrawActiveTexture ( float x , float y , float w , float h , float destW , float destH , float u0 , float v0 , float u1 , float v1 , int uvRotation , bool linearFilter ) {
2015-05-12 19:01:15 +00:00
float texCoords [ 8 ] = {
u0 , v0 ,
u1 , v0 ,
u1 , v1 ,
2015-11-01 21:23:37 +00:00
u0 , v1 ,
2015-05-12 19:01:15 +00:00
} ;
2017-02-15 17:32:44 +00:00
static const GLushort indices [ 4 ] = { 0 , 1 , 3 , 2 } ;
2013-12-06 12:01:34 +00:00
2015-05-12 19:01:15 +00:00
if ( uvRotation ! = ROTATION_LOCKED_HORIZONTAL ) {
float temp [ 8 ] ;
int rotation = 0 ;
2016-01-17 10:01:56 +00:00
// Vertical and Vertical180 needed swapping after we changed the coordinate system.
2015-05-12 19:01:15 +00:00
switch ( uvRotation ) {
case ROTATION_LOCKED_HORIZONTAL180 : rotation = 4 ; break ;
2016-01-17 10:01:56 +00:00
case ROTATION_LOCKED_VERTICAL : rotation = 6 ; break ;
case ROTATION_LOCKED_VERTICAL180 : rotation = 2 ; break ;
2015-05-12 19:01:15 +00:00
}
for ( int i = 0 ; i < 8 ; i + + ) {
temp [ i ] = texCoords [ ( i + rotation ) & 7 ] ;
}
memcpy ( texCoords , temp , sizeof ( temp ) ) ;
}
2013-10-30 13:37:07 +00:00
float pos [ 12 ] = {
x , y , 0 ,
x + w , y , 0 ,
x + w , y + h , 0 ,
x , y + h , 0
} ;
2014-05-08 13:28:25 +00:00
float invDestW = 1.0f / ( destW * 0.5f ) ;
float invDestH = 1.0f / ( destH * 0.5f ) ;
2013-10-30 13:37:07 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2014-05-08 13:28:25 +00:00
pos [ i * 3 ] = pos [ i * 3 ] * invDestW - 1.0f ;
2015-11-01 14:34:53 +00:00
pos [ i * 3 + 1 ] = pos [ i * 3 + 1 ] * invDestH - 1.0f ;
2013-10-30 13:37:07 +00:00
}
2015-10-14 16:32:13 +00:00
// Upscaling postshaders doesn't look well with linear
2017-02-06 15:06:03 +00:00
if ( linearFilter ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
} else {
2015-10-14 16:32:13 +00:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
2013-12-02 16:24:20 +00:00
}
2015-10-31 15:02:25 +00:00
2017-02-15 11:32:48 +00:00
const GLSLProgram * program = glsl_get_program ( ) ;
2017-03-06 16:04:55 +00:00
if ( ! program ) {
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " Trying to DrawActiveTexture() without a program " ) ;
2017-03-06 16:04:55 +00:00
return ;
}
2017-03-13 11:32:21 +00:00
2013-06-28 13:48:36 +00:00
glEnableVertexAttribArray ( program - > a_position ) ;
glEnableVertexAttribArray ( program - > a_texcoord0 ) ;
2015-12-14 06:48:21 +00:00
if ( gstate_c . Supports ( GPU_SUPPORTS_VAO ) ) {
2017-02-04 10:47:19 +00:00
drawEngine_ - > BindBuffer ( pos , sizeof ( pos ) , texCoords , sizeof ( texCoords ) ) ;
drawEngine_ - > BindElementBuffer ( indices , sizeof ( indices ) ) ;
2015-12-14 06:48:21 +00:00
glVertexAttribPointer ( program - > a_position , 3 , GL_FLOAT , GL_FALSE , 12 , 0 ) ;
glVertexAttribPointer ( program - > a_texcoord0 , 2 , GL_FLOAT , GL_FALSE , 8 , ( void * ) sizeof ( pos ) ) ;
2017-02-15 17:32:44 +00:00
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_SHORT , 0 ) ;
2015-12-14 06:48:21 +00:00
} 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 ) ;
2017-02-15 17:32:44 +00:00
glDrawElements ( GL_TRIANGLE_STRIP , 4 , GL_UNSIGNED_SHORT , indices ) ;
2015-12-14 06:48:21 +00:00
}
2013-06-28 13:48:36 +00:00
glDisableVertexAttribArray ( program - > a_position ) ;
glDisableVertexAttribArray ( program - > a_texcoord0 ) ;
2012-11-01 15:19:01 +00:00
}
2013-01-30 20:09:53 +00:00
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : RebindFramebuffer ( ) {
2014-06-07 07:52:58 +00:00
if ( currentRenderVfb_ & & currentRenderVfb_ - > fbo ) {
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( currentRenderVfb_ - > fbo , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2014-08-28 06:08:17 +00:00
} else {
2017-05-16 14:00:34 +00:00
// Should this even happen?
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2014-06-04 07:58:44 +00:00
}
2015-02-03 20:51:39 +00:00
if ( g_Config . iRenderingMode = = FB_NON_BUFFERED_MODE )
glstate . viewport . restore ( ) ;
2014-03-29 23:11:01 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : SetLineWidth ( ) {
2013-10-09 17:00:35 +00:00
# ifndef USING_GLES2
if ( g_Config . iInternalResolution = = 0 ) {
2015-09-19 14:19:03 +00:00
glLineWidth ( std : : max ( 1 , ( int ) ( renderWidth_ / 480 ) ) ) ;
glPointSize ( std : : max ( 1.0f , ( float ) ( renderWidth_ / 480.f ) ) ) ;
2013-10-09 17:00:35 +00:00
} else {
glLineWidth ( g_Config . iInternalResolution ) ;
glPointSize ( ( float ) g_Config . iInternalResolution ) ;
}
# endif
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : ReformatFramebufferFrom ( VirtualFramebuffer * vfb , GEBufferFormat old ) {
2014-09-12 06:30:42 +00:00
if ( ! useBufferedRendering_ | | ! vfb - > fbo ) {
2014-09-07 15:47:04 +00:00
return ;
}
// 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 ) {
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( vfb - > fbo , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : CLEAR } ) ;
} else {
draw_ - > BindFramebufferAsRenderTarget ( vfb - > fbo , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2014-09-07 15:47:04 +00:00
}
RebindFramebuffer ( ) ;
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : BlitFramebufferDepth ( VirtualFramebuffer * src , VirtualFramebuffer * dst ) {
2016-02-11 05:02:19 +00:00
if ( g_Config . bDisableSlowFramebufEffects ) {
return ;
}
2016-01-21 05:35:19 +00:00
bool matchingDepthBuffer = src - > z_address = = dst - > z_address & & src - > z_stride ! = 0 & & dst - > z_stride ! = 0 ;
bool matchingSize = src - > width = = dst - > width & & src - > height = = dst - > height ;
2017-02-21 09:44:52 +00:00
bool matchingRenderSize = src - > renderWidth = = dst - > renderWidth & & src - > renderHeight = = dst - > renderHeight ;
if ( gstate_c . Supports ( GPU_SUPPORTS_ANY_COPY_IMAGE ) & & matchingDepthBuffer & & matchingRenderSize & & matchingSize ) {
draw_ - > CopyFramebufferImage ( src - > fbo , 0 , 0 , 0 , 0 , dst - > fbo , 0 , 0 , 0 , 0 , src - > renderWidth , src - > renderHeight , 1 , Draw : : FB_DEPTH_BIT ) ;
RebindFramebuffer ( ) ;
} else if ( matchingDepthBuffer & & matchingSize ) {
2016-01-21 05:35:19 +00:00
int w = std : : min ( src - > renderWidth , dst - > renderWidth ) ;
int h = std : : min ( src - > renderHeight , dst - > renderHeight ) ;
2014-06-11 07:40:47 +00:00
2015-09-05 20:40:45 +00:00
if ( gstate_c . Supports ( GPU_SUPPORTS_ARB_FRAMEBUFFER_BLIT | GPU_SUPPORTS_NV_FRAMEBUFFER_BLIT ) ) {
2015-07-26 20:54:18 +00:00
// Let's only do this if not clearing depth.
2015-11-04 07:02:02 +00:00
glstate . scissorTest . force ( false ) ;
2017-02-06 10:26:24 +00:00
draw_ - > BlitFramebuffer ( src - > fbo , 0 , 0 , w , h , dst - > fbo , 0 , 0 , w , h , Draw : : FB_DEPTH_BIT , Draw : : FB_BLIT_NEAREST ) ;
2017-02-04 14:30:59 +00:00
// WARNING: If we set dst->depthUpdated here, our optimization above would be pointless.
2015-07-26 20:54:18 +00:00
glstate . scissorTest . restore ( ) ;
2014-01-20 14:02:20 +00:00
}
}
2014-01-20 01:28:11 +00:00
}
2017-02-17 13:30:42 +00:00
void FramebufferManagerGLES : : BindFramebufferAsColorTexture ( int stage , VirtualFramebuffer * framebuffer , int flags ) {
2014-01-20 01:28:11 +00:00
if ( ! framebuffer - > fbo | | ! useBufferedRendering_ ) {
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
gstate_c . skipDrawReason | = SKIPDRAW_BAD_FB_TEXTURE ;
return ;
}
2014-05-23 15:48:29 +00:00
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
// Let's just not bother with the copy in that case.
2015-09-13 18:14:51 +00:00
bool skipCopy = ( flags & BINDFBCOLOR_MAY_COPY ) = = 0 ;
2014-07-09 06:32:41 +00:00
if ( GPUStepping : : IsStepping ( ) | | g_Config . bDisableSlowFramebufEffects ) {
2014-06-13 15:35:12 +00:00
skipCopy = true ;
}
2017-02-17 11:13:53 +00:00
if ( ! skipCopy & & currentRenderVfb_ & & framebuffer - > fb_address = = gstate . getFrameBufRawAddress ( ) ) {
2014-05-30 15:53:22 +00:00
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
2017-02-04 17:46:12 +00:00
Draw : : Framebuffer * renderCopy = GetTempFBO ( framebuffer - > renderWidth , framebuffer - > renderHeight , ( Draw : : FBColorDepth ) framebuffer - > colorDepth ) ;
2014-06-01 05:41:41 +00:00
if ( renderCopy ) {
VirtualFramebuffer copyInfo = * framebuffer ;
copyInfo . fbo = renderCopy ;
2017-04-07 01:49:48 +00:00
CopyFramebufferForColorTexture ( & copyInfo , framebuffer , flags ) ;
2017-02-15 22:56:38 +00:00
draw_ - > BindFramebufferAsTexture ( renderCopy , stage , Draw : : FB_COLOR_BIT , 0 ) ;
2014-06-01 05:41:41 +00:00
} else {
2017-02-15 22:56:38 +00:00
draw_ - > BindFramebufferAsTexture ( framebuffer - > fbo , stage , Draw : : FB_COLOR_BIT , 0 ) ;
2014-05-30 15:53:22 +00:00
}
2014-01-20 01:28:11 +00:00
} else {
2017-02-15 22:56:38 +00:00
draw_ - > BindFramebufferAsTexture ( framebuffer - > fbo , stage , Draw : : FB_COLOR_BIT , 0 ) ;
2014-09-21 19:11:17 +00:00
}
2014-01-20 14:02:20 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : ReadFramebufferToMemory ( VirtualFramebuffer * vfb , bool sync , int x , int y , int w , int h ) {
2015-07-11 22:44:11 +00:00
PROFILE_THIS_SCOPE ( " gpu-readback " ) ;
2013-10-09 09:01:52 +00:00
if ( sync ) {
2015-11-01 17:05:17 +00: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-11 18:30:17 +00:00
}
2013-10-09 09:01:52 +00:00
if ( vfb ) {
2015-11-01 17:05:17 +00:00
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
2016-01-05 04:40:07 +00:00
VirtualFramebuffer * nvfb = FindDownloadTempBuffer ( vfb ) ;
OptimizeDownloadRange ( vfb , x , y , w , h ) ;
2015-11-01 12:32:03 +00:00
BlitFramebuffer ( nvfb , x , y , vfb , x , y , w , h , 0 ) ;
2013-06-25 12:50:35 +00:00
2014-05-08 08:46:19 +00:00
// PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels
// PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs
2015-12-06 17:08:38 +00: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 02:30:25 +00:00
}
2013-08-11 18:30:17 +00:00
}
2014-05-09 18:48:36 +00:00
2017-02-06 11:02:30 +00:00
textureCacheGL_ - > ForgetLastTexture ( ) ;
2014-06-11 07:54:54 +00:00
RebindFramebuffer ( ) ;
2013-06-28 13:48:36 +00:00
}
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : DownloadFramebufferForClut ( u32 fb_address , u32 loadBytes ) {
2016-01-05 05:29:03 +00:00
PROFILE_THIS_SCOPE ( " gpu-readback " ) ;
// Flush async just in case.
PackFramebufferAsync_ ( nullptr ) ;
VirtualFramebuffer * vfb = GetVFBAt ( fb_address ) ;
if ( vfb & & vfb - > fb_stride ! = 0 ) {
const u32 bpp = vfb - > drawnFormat = = GE_FORMAT_8888 ? 4 : 2 ;
int x = 0 ;
int y = 0 ;
int pixels = loadBytes / bpp ;
// The height will be 1 for each stride or part thereof.
int w = std : : min ( pixels % vfb - > fb_stride , ( int ) vfb - > width ) ;
int h = std : : min ( ( pixels + vfb - > fb_stride - 1 ) / vfb - > fb_stride , ( int ) vfb - > height ) ;
2016-01-17 20:30:51 +00:00
// We might still have a pending draw to the fb in question, flush if so.
FlushBeforeCopy ( ) ;
2016-01-05 06:21:33 +00:00
// No need to download if we already have it.
if ( ! vfb - > memoryUpdated & & vfb - > clutUpdatedBytes < loadBytes ) {
// We intentionally don't call OptimizeDownloadRange() here - we don't want to over download.
// CLUT framebuffers are often incorrectly estimated in size.
if ( x = = 0 & & y = = 0 & & w = = vfb - > width & & h = = vfb - > height ) {
vfb - > memoryUpdated = true ;
}
vfb - > clutUpdatedBytes = loadBytes ;
2016-01-05 05:29:03 +00:00
2016-01-05 06:21:33 +00:00
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
VirtualFramebuffer * nvfb = FindDownloadTempBuffer ( vfb ) ;
BlitFramebuffer ( nvfb , x , y , vfb , x , y , w , h , 0 ) ;
2016-01-05 05:29:03 +00:00
2016-01-05 06:21:33 +00:00
PackFramebufferSync_ ( nvfb , x , y , w , h ) ;
2016-01-05 05:29:03 +00:00
2017-02-06 11:02:30 +00:00
textureCacheGL_ - > ForgetLastTexture ( ) ;
2016-01-05 06:21:33 +00:00
RebindFramebuffer ( ) ;
}
2016-01-05 05:29:03 +00:00
}
}
2017-01-21 21:16:30 +00:00
bool FramebufferManagerGLES : : CreateDownloadTempBuffer ( VirtualFramebuffer * nvfb ) {
2016-01-05 04:51:43 +00:00
// When updating VRAM, it need to be exact format.
if ( ! gstate_c . Supports ( GPU_PREFER_CPU_DOWNLOAD ) ) {
switch ( nvfb - > format ) {
case GE_FORMAT_4444 :
2017-02-04 17:46:12 +00:00
nvfb - > colorDepth = Draw : : FBO_4444 ;
2016-01-05 04:51:43 +00:00
break ;
case GE_FORMAT_5551 :
2017-02-04 17:46:12 +00:00
nvfb - > colorDepth = Draw : : FBO_5551 ;
2016-01-05 04:51:43 +00:00
break ;
case GE_FORMAT_565 :
2017-02-04 17:46:12 +00:00
nvfb - > colorDepth = Draw : : FBO_565 ;
2016-01-05 04:51:43 +00:00
break ;
case GE_FORMAT_8888 :
default :
2017-02-04 17:46:12 +00:00
nvfb - > colorDepth = Draw : : FBO_8888 ;
2016-01-05 04:51:43 +00:00
break ;
}
}
2017-02-06 10:26:24 +00:00
nvfb - > fbo = draw_ - > CreateFramebuffer ( { nvfb - > width , nvfb - > height , 1 , 1 , false , ( Draw : : FBColorDepth ) nvfb - > colorDepth } ) ;
2017-02-04 16:44:31 +00:00
if ( ! nvfb - > fbo ) {
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " Error creating GL FBO! %i x %i " , nvfb - > renderWidth , nvfb - > renderHeight ) ;
2016-01-05 04:51:43 +00:00
return false ;
}
return true ;
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : UpdateDownloadTempBuffer ( VirtualFramebuffer * nvfb ) {
2016-01-05 04:57:54 +00:00
_assert_msg_ ( G3D , nvfb - > fbo , " Expecting a valid nvfb in UpdateDownloadTempBuffer " ) ;
// Discard the previous contents of this buffer where possible.
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( nvfb - > fbo , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } ) ;
2016-01-05 04:57:54 +00:00
GLenum attachments [ 3 ] = { GL_COLOR_ATTACHMENT0 , GL_STENCIL_ATTACHMENT , GL_DEPTH_ATTACHMENT } ;
glInvalidateFramebuffer ( GL_FRAMEBUFFER , 3 , attachments ) ;
} else if ( gl_extensions . IsGLES ) {
2017-05-31 11:38:22 +00:00
draw_ - > BindFramebufferAsRenderTarget ( nvfb - > fbo , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : CLEAR } ) ;
2016-01-05 04:51:43 +00:00
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2016-01-05 04:51:43 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : BlitFramebuffer ( VirtualFramebuffer * dst , int dstX , int dstY , VirtualFramebuffer * src , int srcX , int srcY , int w , int h , int bpp ) {
2014-07-10 06:40:29 +00:00
if ( ! dst - > fbo | | ! src - > fbo | | ! useBufferedRendering_ ) {
// This can happen if they recently switched from non-buffered.
2017-05-16 14:00:34 +00:00
if ( useBufferedRendering_ )
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2014-05-08 13:16:33 +00:00
return ;
}
2015-09-05 23:28:59 +00: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 09:01:52 +00:00
2014-06-05 07:14:31 +00: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 21:23:37 +00:00
int srcY1 = srcY * srcYFactor ;
int srcY2 = ( srcY + h ) * srcYFactor ;
2014-06-05 07:14:31 +00: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-10-31 23:13:37 +00:00
int dstY1 = dstY * dstYFactor ;
int dstY2 = ( dstY + h ) * dstYFactor ;
2014-05-25 22:13:19 +00:00
2015-12-30 18:27:18 +00: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 18:39:21 +00:00
if ( gstate_c . Supports ( GPU_SUPPORTS_ANY_COPY_IMAGE ) ) {
2015-12-29 06:23:07 +00: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 18:27:18 +00:00
const bool sameDepth = dst - > colorDepth = = src - > colorDepth ;
2015-12-29 06:23:07 +00:00
const bool srcInsideBounds = srcX2 < = src - > renderWidth & & srcY2 < = src - > renderHeight ;
const bool dstInsideBounds = dstX2 < = dst - > renderWidth & & dstY2 < = dst - > renderHeight ;
2015-12-30 18:27:18 +00: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 ) ) {
2017-02-12 10:20:55 +00:00
draw_ - > CopyFramebufferImage ( src - > fbo , 0 , srcX1 , srcY1 , 0 , dst - > fbo , 0 , dstX1 , dstY1 , 0 , dstX2 - dstX1 , dstY2 - dstY1 , 1 , Draw : : FB_COLOR_BIT ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2015-12-06 18:39:21 +00:00
return ;
}
}
glstate . scissorTest . force ( false ) ;
2014-06-05 07:14:31 +00:00
if ( useBlit ) {
2017-02-06 10:26:24 +00:00
draw_ - > BlitFramebuffer ( src - > fbo , srcX1 , srcY1 , srcX2 , srcY2 , dst - > fbo , dstX1 , dstY1 , dstX2 , dstY2 , Draw : : FB_COLOR_BIT , Draw : : FB_BLIT_NEAREST ) ;
2014-05-09 14:01:30 +00:00
} else {
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( dst - > fbo , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferAsTexture ( src - > fbo , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
2014-05-09 14:01:30 +00:00
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
CompileDraw2DProgram ( ) ;
2015-11-15 21:12:29 +00: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 14:01:30 +00:00
// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
// Should maybe revamp that interface.
2014-06-02 04:20:04 +00:00
float srcW = src - > bufferWidth ;
float srcH = src - > bufferHeight ;
2017-02-15 11:32:48 +00:00
glsl_bind ( draw2dprogram_ ) ;
DrawActiveTexture ( dstX1 , dstY1 , w * dstXFactor , h , dst - > bufferWidth , dst - > bufferHeight , srcX1 / srcW , srcY1 / srcH , srcX2 / srcW , srcY2 / srcH , ROTATION_LOCKED_HORIZONTAL , false ) ;
2014-05-09 14:01:30 +00:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2017-02-06 11:02:30 +00:00
textureCacheGL_ - > ForgetLastTexture ( ) ;
2014-08-28 06:07:11 +00:00
glstate . viewport . restore ( ) ;
2015-11-15 21:12:29 +00: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 14:01:30 +00:00
}
2013-10-09 09:01:52 +00:00
2014-05-27 09:00:28 +00:00
glstate . scissorTest . restore ( ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-06-28 13:48:36 +00:00
}
2013-07-06 09:09:08 +00:00
// TODO: SSE/NEON
2013-10-23 12:46:04 +00:00
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
2014-09-13 23:37:59 +00:00
void ConvertFromRGBA8888 ( u8 * dst , const u8 * src , u32 dstStride , u32 srcStride , u32 width , u32 height , GEBufferFormat format ) {
2014-06-16 02:33:32 +00: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 18:53:32 +00:00
2013-10-09 09:01:52 +00:00
if ( format = = GE_FORMAT_8888 ) {
2014-06-16 02:33:32 +00:00
u32 * dst32 = ( u32 * ) dst ;
2013-10-09 09:01:52 +00:00
if ( src = = dst ) {
2013-07-02 13:08:59 +00:00
return ;
2014-05-08 08:46:19 +00:00
} else {
// Here let's assume they don't intersect
2014-06-16 02:33:32 +00:00
for ( u32 y = 0 ; y < height ; + + y ) {
memcpy ( dst32 , src32 , width * 4 ) ;
2014-09-13 23:37:59 +00:00
src32 + = srcStride ;
dst32 + = dstStride ;
2014-06-16 02:33:32 +00:00
}
2013-07-02 13:08:59 +00:00
}
2014-05-08 08:46:19 +00:00
} else {
// But here it shouldn't matter if they do intersect
2013-07-05 01:31:31 +00:00
u16 * dst16 = ( u16 * ) dst ;
switch ( format ) {
case GE_FORMAT_565 : // BGR 565
2017-03-26 09:39:25 +00:00
{
2014-06-16 02:33:32 +00:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 17:59:12 +00:00
ConvertRGBA8888ToRGB565 ( dst16 , src32 , width ) ;
2014-09-13 23:37:59 +00:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 07:18:01 +00:00
}
2013-07-05 01:31:31 +00:00
}
break ;
case GE_FORMAT_5551 : // ABGR 1555
2017-03-26 09:39:25 +00:00
{
2014-06-16 02:33:32 +00:00
for ( u32 y = 0 ; y < height ; + + y ) {
ConvertRGBA8888ToRGBA5551 ( dst16 , src32 , width ) ;
2014-09-13 23:37:59 +00:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-06-16 02:33:32 +00:00
}
2013-07-05 01:31:31 +00:00
}
break ;
case GE_FORMAT_4444 : // ABGR 4444
2017-03-26 09:39:25 +00:00
{
2014-06-16 02:33:32 +00:00
for ( u32 y = 0 ; y < height ; + + y ) {
2015-04-08 17:59:12 +00:00
ConvertRGBA8888ToRGBA4444 ( dst16 , src32 , width ) ;
2014-09-13 23:37:59 +00:00
src32 + = srcStride ;
dst16 + = dstStride ;
2014-05-04 07:18:01 +00:00
}
2013-07-05 01:31:31 +00:00
}
break ;
2013-07-30 06:05:59 +00:00
case GE_FORMAT_8888 :
2014-05-04 07:18:01 +00:00
case GE_FORMAT_INVALID :
2013-07-30 06:05:59 +00:00
// Not possible.
break ;
2013-06-25 12:50:35 +00:00
}
2013-06-28 13:48:36 +00:00
}
}
2015-12-06 17:08:38 +00:00
# ifdef DEBUG_READ_PIXELS
2014-05-08 08:43:46 +00:00
// TODO: Make more generic.
static void LogReadPixelsError ( GLenum error ) {
switch ( error ) {
2014-05-08 12:30:40 +00:00
case GL_NO_ERROR :
2014-05-08 08:43:46 +00:00
break ;
case GL_INVALID_ENUM :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_INVALID_ENUM " ) ;
2014-05-08 08:43:46 +00:00
break ;
case GL_INVALID_VALUE :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_INVALID_VALUE " ) ;
2014-05-08 08:43:46 +00:00
break ;
case GL_INVALID_OPERATION :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_INVALID_OPERATION " ) ;
2014-05-08 08:43:46 +00:00
break ;
case GL_INVALID_FRAMEBUFFER_OPERATION :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION " ) ;
2014-05-08 08:43:46 +00:00
break ;
2014-05-08 12:30:40 +00:00
case GL_OUT_OF_MEMORY :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_OUT_OF_MEMORY " ) ;
2014-05-08 12:30:40 +00:00
break ;
2015-12-06 17:08:38 +00:00
# ifndef USING_GLES2
2014-05-08 12:30:40 +00:00
case GL_STACK_UNDERFLOW :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_STACK_UNDERFLOW " ) ;
2014-05-08 12:30:40 +00:00
break ;
case GL_STACK_OVERFLOW :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: GL_STACK_OVERFLOW " ) ;
2014-05-08 08:43:46 +00:00
break ;
2015-12-06 17:08:38 +00:00
# endif
default :
2017-03-13 11:32:21 +00:00
ERROR_LOG ( FRAMEBUF , " glReadPixels: %08x " , error ) ;
2015-12-06 17:08:38 +00:00
break ;
2014-05-08 08:43:46 +00:00
}
}
2015-12-06 17:08:38 +00:00
# endif
2014-05-08 08:43:46 +00:00
2015-12-06 17:18:37 +00:00
static void SafeGLReadPixels ( GLint x , GLint y , GLsizei w , GLsizei h , GLenum fmt , GLenum type , void * pixels ) {
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2016-02-28 10:06:49 +00:00
if ( ! gl_extensions . IsGLES | | ( gl_extensions . GLES3 & & gl_extensions . gpuVendor ! = GPU_VENDOR_NVIDIA ) ) {
2015-12-06 17:18:37 +00:00
// Some drivers seem to require we specify this. See #8254.
glPixelStorei ( GL_PACK_ROW_LENGTH , w ) ;
}
2015-12-13 05:21:38 +00:00
glReadPixels ( x , y , w , h , fmt , type , pixels ) ;
2015-12-06 17:18:37 +00:00
# ifdef DEBUG_READ_PIXELS
LogReadPixelsError ( glGetError ( ) ) ;
# endif
if ( ! gl_extensions . IsGLES | | gl_extensions . GLES3 ) {
glPixelStorei ( GL_PACK_ROW_LENGTH , 0 ) ;
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2015-12-06 17:18:37 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : PackFramebufferAsync_ ( VirtualFramebuffer * vfb ) {
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-08-11 18:30:17 +00:00
const int MAX_PBO = 2 ;
2013-06-28 13:48:36 +00:00
GLubyte * packed = 0 ;
2013-07-02 13:08:59 +00:00
bool unbind = false ;
2014-08-30 09:05:51 +00:00
const u8 nextPBO = ( currentPBO_ + 1 ) % MAX_PBO ;
2015-09-05 17:58:47 +00:00
const bool useCPU = gstate_c . Supports ( GPU_PREFER_CPU_DOWNLOAD ) ;
2013-08-11 18:30:17 +00:00
2013-07-04 14:24:37 +00:00
// We'll prepare two PBOs to switch between readying and reading
2013-10-09 09:01:52 +00:00
if ( ! pixelBufObj_ ) {
2015-12-06 17:08:38 +00: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-11 18:30:17 +00:00
GLuint pbos [ MAX_PBO ] ;
glGenBuffers ( MAX_PBO , pbos ) ;
pixelBufObj_ = new AsyncPBO [ MAX_PBO ] ;
2013-10-09 09:01:52 +00:00
for ( int i = 0 ; i < MAX_PBO ; i + + ) {
2013-08-11 18:30:17 +00:00
pixelBufObj_ [ i ] . handle = pbos [ i ] ;
pixelBufObj_ [ i ] . maxSize = 0 ;
pixelBufObj_ [ i ] . reading = false ;
}
}
// Receive previously requested data from a PBO
2014-05-04 07:18:01 +00:00
AsyncPBO & pbo = pixelBufObj_ [ nextPBO ] ;
if ( pbo . reading ) {
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo . handle ) ;
2015-12-06 17:08:38 +00: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-11 18:30:17 +00:00
packed = ( GLubyte * ) glMapBuffer ( GL_PIXEL_PACK_BUFFER , GL_READ_ONLY ) ;
2015-12-06 17:08:38 +00:00
# endif
2013-07-02 13:08:59 +00:00
2013-10-09 09:01:52 +00:00
if ( packed ) {
2017-03-13 11:32:21 +00:00
DEBUG_LOG ( FRAMEBUF , " Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u " ,
2014-05-04 07:18:01 +00:00
pbo . size , packed , pbo . fb_address , pbo . stride , nextPBO ) ;
2013-08-11 18:30:17 +00:00
2017-03-26 09:39:25 +00:00
if ( useCPU ) {
2014-05-04 07:18:01 +00:00
u8 * dst = Memory : : GetPointer ( pbo . fb_address ) ;
2014-09-13 23:37:59 +00:00
ConvertFromRGBA8888 ( dst , packed , pbo . stride , pbo . stride , pbo . stride , pbo . height , pbo . format ) ;
2013-10-09 09:01:52 +00:00
} else {
2013-09-22 09:03:39 +00:00
// We don't need to convert, GPU already did (or should have)
2015-04-06 01:03:50 +00:00
Memory : : MemcpyUnchecked ( pbo . fb_address , packed , pbo . size ) ;
2013-08-11 18:30:17 +00:00
}
2013-07-02 13:08:59 +00:00
2014-05-04 07:18:01 +00:00
pbo . reading = false ;
2013-08-11 18:30:17 +00:00
}
2013-07-02 13:08:59 +00:00
2013-08-11 18:30:17 +00:00
glUnmapBuffer ( GL_PIXEL_PACK_BUFFER ) ;
unbind = true ;
2013-07-02 13:08:59 +00:00
}
2013-06-28 13:48:36 +00:00
// Order packing/readback of the framebuffer
2013-10-09 09:01:52 +00:00
if ( vfb ) {
2013-06-25 12:50:35 +00:00
int pixelType , pixelSize , pixelFormat , align ;
2015-09-05 17:58:47 +00:00
bool reverseOrder = gstate_c . Supports ( GPU_PREFER_REVERSE_COLOR_ORDER ) ;
2013-06-25 12:50:35 +00:00
switch ( vfb - > format ) {
2013-07-04 14:24:37 +00:00
// GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC)
// GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value)
// We want R G B A, so we use *_REV for 16-bit formats and GL_UNSIGNED_BYTE for 32-bit
case GE_FORMAT_4444 : // 16 bit RGBA
2015-12-06 17:08:38 +00:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_4_4_4_4 ;
# else
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4 ) ;
2015-12-06 17:08:38 +00:00
# endif
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
2014-07-04 21:01:56 +00:00
align = 2 ;
2013-06-25 18:14:10 +00:00
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_5551 : // 16 bit RGBA
2015-12-06 17:08:38 +00:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_5_5_5_1 ;
# else
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1 ) ;
2015-12-06 17:08:38 +00:00
# endif
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
2014-07-04 21:01:56 +00:00
align = 2 ;
2013-06-25 18:14:10 +00:00
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_565 : // 16 bit RGB
2015-12-06 17:08:38 +00:00
# ifdef USING_GLES2
pixelType = GL_UNSIGNED_SHORT_5_6_5 ;
# else
2013-08-19 18:18:32 +00:00
pixelType = ( reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5 ) ;
2015-12-06 17:08:38 +00:00
# endif
2013-07-04 14:24:37 +00:00
pixelFormat = GL_RGB ;
2013-06-25 18:14:10 +00:00
pixelSize = 2 ;
2014-07-04 21:01:56 +00:00
align = 2 ;
2013-06-25 18:14:10 +00:00
break ;
2013-07-04 14:24:37 +00:00
case GE_FORMAT_8888 : // 32 bit RGBA
2013-06-28 13:48:36 +00:00
default :
pixelType = GL_UNSIGNED_BYTE ;
2017-03-26 09:39:25 +00:00
pixelFormat = GL_RGBA ;
2013-06-25 18:14:10 +00:00
pixelSize = 4 ;
align = 4 ;
break ;
2013-06-25 12:50:35 +00:00
}
2014-05-04 03:20:40 +00:00
// If using the CPU, we need 4 bytes per pixel always.
u32 bufSize = vfb - > fb_stride * vfb - > height * ( useCPU ? 4 : pixelSize ) ;
2013-09-19 17:28:14 +00:00
u32 fb_address = ( 0x04000000 ) | vfb - > fb_address ;
2013-06-28 13:48:36 +00:00
2013-08-11 18:30:17 +00:00
if ( vfb - > fbo ) {
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2013-08-11 18:30:17 +00:00
} else {
2013-12-22 08:03:39 +00:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferAsync_: vfb->fbo == 0 " ) ;
2013-08-11 18:30:17 +00:00
return ;
}
2013-06-28 13:48:36 +00:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pixelBufObj_ [ currentPBO_ ] . handle ) ;
2013-10-09 09:01:52 +00:00
if ( pixelBufObj_ [ currentPBO_ ] . maxSize < bufSize ) {
2013-07-04 14:24:37 +00:00
// We reserve a buffer big enough to fit all those pixels
2014-05-04 03:20:40 +00:00
glBufferData ( GL_PIXEL_PACK_BUFFER , bufSize , NULL , GL_DYNAMIC_READ ) ;
2013-06-28 13:48:36 +00:00
pixelBufObj_ [ currentPBO_ ] . maxSize = bufSize ;
2013-06-25 12:50:35 +00:00
}
2013-10-09 09:01:52 +00:00
if ( useCPU ) {
2013-07-04 14:24:37 +00:00
// If converting pixel formats on the CPU we'll always request RGBA8888
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2017-03-26 09:39:25 +00:00
SafeGLReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2013-06-28 13:48:36 +00:00
} else {
2013-07-04 14:24:37 +00:00
// Otherwise we'll directly request the format we need and let the GPU sort it out
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , align ) ;
2015-12-06 17:18:37 +00:00
SafeGLReadPixels ( 0 , 0 , vfb - > fb_stride , vfb - > height , pixelFormat , pixelType , 0 ) ;
2013-06-28 13:48:36 +00:00
}
2013-07-02 13:08:59 +00:00
unbind = true ;
2013-06-28 13:48:36 +00:00
2013-08-11 18:30:17 +00:00
pixelBufObj_ [ currentPBO_ ] . fb_address = fb_address ;
pixelBufObj_ [ currentPBO_ ] . size = bufSize ;
pixelBufObj_ [ currentPBO_ ] . stride = vfb - > fb_stride ;
pixelBufObj_ [ currentPBO_ ] . height = vfb - > height ;
pixelBufObj_ [ currentPBO_ ] . format = vfb - > format ;
pixelBufObj_ [ currentPBO_ ] . reading = true ;
2013-06-28 13:48:36 +00:00
}
currentPBO_ = nextPBO ;
2013-10-09 09:01:52 +00:00
if ( unbind ) {
2013-07-02 13:08:59 +00:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
2013-06-28 13:48:36 +00:00
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-06-28 13:48:36 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : PackFramebufferSync_ ( VirtualFramebuffer * vfb , int x , int y , int w , int h ) {
2013-09-22 09:03:39 +00:00
if ( vfb - > fbo ) {
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2013-07-02 13:08:59 +00:00
} else {
2013-09-22 09:03:39 +00:00
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackFramebufferSync_: vfb->fbo == 0 " ) ;
2013-07-02 13:08:59 +00:00
return ;
}
2016-02-12 06:30:44 +00:00
int possibleH = std : : max ( vfb - > height - y , 0 ) ;
if ( h > possibleH ) {
h = possibleH ;
}
2013-06-28 13:48:36 +00:00
2016-02-12 06:30:44 +00:00
// Pixel size always 4 here because we always request RGBA8888
u32 bufSize = vfb - > fb_stride * h * 4 ;
u32 fb_address = 0x04000000 | vfb - > fb_address ;
2014-05-09 19:01:17 +00:00
2017-03-26 09:39:25 +00:00
bool convert = vfb - > format ! = GE_FORMAT_8888 ;
2014-06-16 02:33:32 +00:00
const int dstBpp = vfb - > format = = GE_FORMAT_8888 ? 4 : 2 ;
2016-01-18 20:57:37 +00:00
const int packWidth = std : : min ( vfb - > fb_stride , std : : min ( x + w , ( int ) vfb - > width ) ) ;
2014-05-09 19:01:17 +00:00
2016-02-12 06:30:44 +00:00
int dstByteOffset = y * vfb - > fb_stride * dstBpp ;
u8 * dst = Memory : : GetPointer ( fb_address + dstByteOffset ) ;
2016-02-11 21:09:32 +00:00
2016-02-12 06:30:44 +00:00
GLubyte * packed = nullptr ;
2014-05-09 19:26:46 +00:00
if ( ! convert ) {
2016-02-12 06:30:44 +00:00
packed = ( GLubyte * ) dst ;
2016-01-18 20:57:37 +00:00
} else {
// End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
if ( ! convBuf_ | | convBufSize_ < bufSize ) {
2014-05-26 02:48:46 +00:00
delete [ ] convBuf_ ;
2016-01-18 20:57:37 +00:00
convBuf_ = new u8 [ bufSize ] ;
convBufSize_ = bufSize ;
2014-05-26 02:48:46 +00:00
}
packed = convBuf_ ;
2013-06-28 13:48:36 +00:00
}
2013-10-09 09:01:52 +00:00
if ( packed ) {
2017-03-13 11:32:21 +00:00
DEBUG_LOG ( FRAMEBUF , " Reading framebuffer to mem, bufSize = %u, fb_address = %08x " , bufSize , fb_address ) ;
2013-06-28 13:48:36 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2014-05-04 15:43:30 +00:00
GLenum glfmt = GL_RGBA ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2014-06-02 02:08:49 +00:00
2016-01-18 20:57:37 +00:00
SafeGLReadPixels ( 0 , y , h = = 1 ? packWidth : vfb - > fb_stride , h , glfmt , GL_UNSIGNED_BYTE , packed ) ;
2013-06-28 13:48:36 +00:00
2014-05-09 19:01:17 +00:00
if ( convert ) {
2016-02-12 06:30:44 +00:00
ConvertFromRGBA8888 ( dst , packed , vfb - > fb_stride , vfb - > fb_stride , packWidth , h , vfb - > format ) ;
2013-06-28 13:48:36 +00:00
}
}
2015-03-14 23:11:20 +00:00
if ( gl_extensions . GLES3 & & glInvalidateFramebuffer ! = nullptr ) {
# ifdef USING_GLES2
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
2017-05-22 12:48:20 +00:00
draw_ - > BindFramebufferAsRenderTarget ( vfb - > fbo , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } ) ;
2015-03-14 23:11:20 +00:00
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 ) ;
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-06-25 12:50:35 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : PackDepthbuffer ( VirtualFramebuffer * vfb , int x , int y , int w , int h ) {
2016-01-18 20:57:37 +00:00
if ( vfb - > fbo ) {
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2016-01-18 20:57:37 +00:00
} else {
ERROR_LOG_REPORT_ONCE ( vfbfbozero , SCEGE , " PackDepthbuffer: vfb->fbo == 0 " ) ;
return ;
}
// Pixel size always 4 here because we always request float
const u32 bufSize = vfb - > z_stride * ( h - y ) * 4 ;
const u32 z_address = ( 0x04000000 ) | vfb - > z_address ;
const int packWidth = std : : min ( vfb - > z_stride , std : : min ( x + w , ( int ) vfb - > width ) ) ;
if ( ! convBuf_ | | convBufSize_ < bufSize ) {
delete [ ] convBuf_ ;
convBuf_ = new u8 [ bufSize ] ;
convBufSize_ = bufSize ;
}
2017-03-13 11:32:21 +00:00
DEBUG_LOG ( FRAMEBUF , " Reading depthbuffer to mem at %08x for vfb=%08x " , z_address , vfb - > fb_address ) ;
2016-01-18 20:57:37 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
SafeGLReadPixels ( 0 , y , h = = 1 ? packWidth : vfb - > z_stride , h , GL_DEPTH_COMPONENT , GL_FLOAT , convBuf_ ) ;
int dstByteOffset = y * vfb - > fb_stride * sizeof ( u16 ) ;
u16 * depth = ( u16 * ) Memory : : GetPointer ( z_address + dstByteOffset ) ;
GLfloat * packed = ( GLfloat * ) convBuf_ ;
int totalPixels = h = = 1 ? packWidth : vfb - > z_stride * h ;
for ( int i = 0 ; i < totalPixels ; + + i ) {
float scaled = FromScaledDepth ( packed [ i ] ) ;
if ( scaled < = 0.0f ) {
depth [ i ] = 0 ;
} else if ( scaled > = 65535.0f ) {
depth [ i ] = 65535 ;
} else {
depth [ i ] = ( int ) scaled ;
}
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2016-01-18 20:57:37 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : EndFrame ( ) {
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2017-02-15 11:20:50 +00:00
2014-05-08 12:26:17 +00:00
// We flush to memory last requested framebuffer, if any.
// Only do this in the read-framebuffer modes.
2014-05-27 06:26:29 +00:00
if ( updateVRAM_ )
2015-12-06 17:08:38 +00:00
PackFramebufferAsync_ ( nullptr ) ;
2015-03-14 23:02:04 +00: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 ;
}
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( temp . second . fbo , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } ) ;
2015-03-14 23:02:04 +00:00
GLenum attachments [ 3 ] = { GL_COLOR_ATTACHMENT0 , GL_STENCIL_ATTACHMENT , GL_DEPTH_ATTACHMENT } ;
glInvalidateFramebuffer ( GL_FRAMEBUFFER , 3 , attachments ) ;
}
2017-05-16 14:00:34 +00:00
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } ) ;
2015-03-14 23:02:04 +00:00
}
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-01-30 20:09:53 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : DeviceLost ( ) {
2017-04-14 06:07:21 +00:00
DestroyAllFBOs ( ) ;
2013-09-26 10:41:07 +00:00
DestroyDraw2DProgram ( ) ;
2013-06-11 09:28:41 +00:00
}
2017-01-21 21:16:30 +00:00
std : : vector < FramebufferInfo > FramebufferManagerGLES : : GetFramebufferList ( ) {
2013-02-17 00:06:06 +00:00
std : : vector < FramebufferInfo > list ;
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2013-02-17 00:06:06 +00:00
FramebufferInfo info ;
info . fb_address = vfb - > fb_address ;
info . z_address = vfb - > z_address ;
info . format = vfb - > format ;
info . width = vfb - > width ;
info . height = vfb - > height ;
info . fbo = vfb - > fbo ;
list . push_back ( info ) ;
}
return list ;
}
2017-04-14 06:07:21 +00:00
void FramebufferManagerGLES : : DestroyAllFBOs ( ) {
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-06-09 09:11:16 +00:00
currentRenderVfb_ = 0 ;
2013-06-10 20:07:48 +00:00
displayFramebuf_ = 0 ;
prevDisplayFramebuf_ = 0 ;
prevPrevDisplayFramebuf_ = 0 ;
2013-06-09 09:11:16 +00:00
2013-06-23 15:51:35 +00:00
for ( size_t i = 0 ; i < vfbs_ . size ( ) ; + + i ) {
VirtualFramebuffer * vfb = vfbs_ [ i ] ;
2017-03-13 11:32:21 +00:00
INFO_LOG ( FRAMEBUF , " Destroying FBO for %08x : %i x %i x %i " , vfb - > fb_address , vfb - > width , vfb - > height , vfb - > format ) ;
2013-06-23 15:16:22 +00:00
DestroyFramebuf ( vfb ) ;
2013-01-30 20:09:53 +00:00
}
vfbs_ . clear ( ) ;
2014-07-05 06:27:12 +00: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 ) {
2017-02-04 17:46:12 +00:00
delete it - > second . fbo ;
2014-07-05 06:27:12 +00:00
}
tempFBOs_ . clear ( ) ;
2014-08-04 01:45:36 +00:00
DisableState ( ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-01-30 20:09:53 +00:00
}
2013-01-31 11:14:57 +00:00
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : FlushBeforeCopy ( ) {
2014-06-11 07:28:28 +00:00
// 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 00:43:40 +00: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.
2017-01-23 22:25:09 +00:00
SetRenderFrameBuffer ( gstate_c . IsDirty ( DIRTY_FRAMEBUF ) , gstate_c . skipDrawReason ) ;
2017-02-04 10:47:19 +00:00
drawEngine_ - > Flush ( ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2014-06-11 07:28:28 +00:00
}
2017-01-21 21:16:30 +00:00
void FramebufferManagerGLES : : Resized ( ) {
2017-04-24 18:58:16 +00:00
FramebufferManagerCommon : : Resized ( ) ;
if ( UpdateSize ( ) ) {
DestroyAllFBOs ( ) ;
}
DestroyDraw2DProgram ( ) ;
SetLineWidth ( ) ;
if ( ! draw2dprogram_ ) {
CompileDraw2DProgram ( ) ;
}
2013-01-31 11:14:57 +00:00
}
2013-09-23 02:03:31 +00:00
2017-01-21 21:16:30 +00:00
bool FramebufferManagerGLES : : GetFramebuffer ( u32 fb_address , int fb_stride , GEBufferFormat format , GPUDebugBuffer & buffer , int maxRes ) {
2013-09-23 02:03:31 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2015-08-05 00:43:40 +00:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , format ) ;
2013-09-23 02:03:31 +00:00
return true ;
}
2016-06-12 14:09:01 +00:00
int w = vfb - > renderWidth , h = vfb - > renderHeight ;
if ( vfb - > fbo ) {
if ( maxRes > 0 & & vfb - > renderWidth > vfb - > width * maxRes ) {
w = vfb - > width * maxRes ;
h = vfb - > height * maxRes ;
2017-02-04 17:46:12 +00:00
Draw : : Framebuffer * tempFBO = GetTempFBO ( w , h ) ;
2016-06-12 14:09:01 +00:00
VirtualFramebuffer tempVfb = * vfb ;
tempVfb . fbo = tempFBO ;
tempVfb . bufferWidth = vfb - > width ;
tempVfb . bufferHeight = vfb - > height ;
tempVfb . renderWidth = w ;
tempVfb . renderHeight = h ;
BlitFramebuffer ( & tempVfb , 0 , 0 , vfb , 0 , 0 , vfb - > width , vfb - > height , 0 ) ;
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( tempFBO ) ;
2016-06-12 14:09:01 +00:00
} else {
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2016-06-12 14:09:01 +00:00
}
}
buffer . Allocate ( w , h , GE_FORMAT_8888 , ! useBufferedRendering_ , true ) ;
2015-09-05 21:39:10 +00:00
if ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES )
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
2013-09-23 02:03:31 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2016-06-12 14:09:01 +00:00
SafeGLReadPixels ( 0 , 0 , w , h , GL_RGBA , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2017-02-06 23:38:12 +00:00
// We may have blitted to a temp FBO.
2016-08-06 22:56:03 +00:00
RebindFramebuffer ( ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-09-23 02:03:31 +00:00
return true ;
}
2013-09-28 09:14:27 +00:00
2017-01-21 21:16:30 +00:00
bool FramebufferManagerGLES : : GetOutputFramebuffer ( GPUDebugBuffer & buffer ) {
2015-09-19 14:19:03 +00:00
int pw = PSP_CoreParameter ( ) . pixelWidth ;
int ph = PSP_CoreParameter ( ) . pixelHeight ;
2015-11-03 03:43:36 +00:00
// The backbuffer is flipped.
buffer . Allocate ( pw , ph , GPU_DBG_FORMAT_888_RGB , true ) ;
2014-12-20 16:31:56 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
2015-12-06 17:18:37 +00:00
SafeGLReadPixels ( 0 , 0 , pw , ph , GL_RGB , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2014-12-20 16:31:56 +00:00
return true ;
}
2017-01-21 21:16:30 +00:00
bool FramebufferManagerGLES : : GetDepthbuffer ( u32 fb_address , int fb_stride , u32 z_address , int z_stride , GPUDebugBuffer & buffer ) {
2013-09-28 09:14:27 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2013-10-06 00:19:17 +00:00
buffer = GPUDebugBuffer ( Memory : : GetPointer ( z_address | 0x04000000 ) , z_stride , 512 , GPU_DBG_FORMAT_16BIT ) ;
2013-09-28 09:14:27 +00:00
return true ;
}
2017-02-23 15:27:26 +00:00
if ( ! vfb - > fbo ) {
return false ;
}
2016-01-18 09:30:05 +00:00
if ( gstate_c . Supports ( GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT ) ) {
2016-03-26 23:11:14 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_FLOAT_DIV_256 , ! useBufferedRendering_ ) ;
2016-01-18 09:30:05 +00:00
} else {
2016-03-26 23:11:14 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_FLOAT , ! useBufferedRendering_ ) ;
2016-01-18 09:30:05 +00:00
}
2013-09-29 05:03:36 +00:00
if ( vfb - > fbo )
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2015-09-05 21:39:10 +00:00
if ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES )
2016-01-03 23:11:19 +00:00
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
2013-09-28 09:14:27 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 4 ) ;
2015-12-06 17:18:37 +00:00
SafeGLReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_DEPTH_COMPONENT , GL_FLOAT , buffer . GetData ( ) ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-09-28 09:14:27 +00:00
return true ;
}
2017-01-21 21:16:30 +00:00
bool FramebufferManagerGLES : : GetStencilbuffer ( u32 fb_address , int fb_stride , GPUDebugBuffer & buffer ) {
2013-09-28 09:14:27 +00:00
VirtualFramebuffer * vfb = currentRenderVfb_ ;
if ( ! vfb ) {
vfb = GetVFBAt ( fb_address ) ;
}
if ( ! vfb ) {
// If there's no vfb and we're drawing there, must be memory?
2013-10-06 00:19:17 +00:00
// TODO: Actually get the stencil.
buffer = GPUDebugBuffer ( Memory : : GetPointer ( fb_address | 0x04000000 ) , fb_stride , 512 , GPU_DBG_FORMAT_8888 ) ;
2013-09-28 09:14:27 +00:00
return true ;
}
# ifndef USING_GLES2
2016-03-26 23:11:14 +00:00
buffer . Allocate ( vfb - > renderWidth , vfb - > renderHeight , GPU_DBG_FORMAT_8BIT , ! useBufferedRendering_ ) ;
2013-09-29 05:03:36 +00:00
if ( vfb - > fbo )
2017-02-06 10:26:24 +00:00
draw_ - > BindFramebufferForRead ( vfb - > fbo ) ;
2016-01-03 23:11:19 +00:00
if ( gl_extensions . GLES3 | | ! gl_extensions . IsGLES )
glReadBuffer ( GL_COLOR_ATTACHMENT0 ) ;
2013-09-28 09:14:27 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 2 ) ;
2015-12-06 17:18:37 +00:00
SafeGLReadPixels ( 0 , 0 , vfb - > renderWidth , vfb - > renderHeight , GL_STENCIL_INDEX , GL_UNSIGNED_BYTE , buffer . GetData ( ) ) ;
2017-03-03 13:15:27 +00:00
CHECK_GL_ERROR_IF_DEBUG ( ) ;
2013-09-28 09:14:27 +00:00
return true ;
# else
return false ;
# endif
}