2020-05-09 22:06:22 -07:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// 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/.
# include <cmath>
2020-05-10 16:53:15 -07:00
# include <set>
2020-05-15 18:07:07 +02:00
# include <cstdint>
2023-04-09 22:47:34 +02:00
# include <algorithm>
2020-05-31 20:13:35 +02:00
2020-10-04 23:24:14 +02:00
# include "Common/GPU/thin3d.h"
2020-10-04 00:25:21 +02:00
2020-10-04 10:30:18 +02:00
# include "Common/System/Display.h"
# include "Common/System/System.h"
2023-06-30 17:15:49 +02:00
# include "Common/System/OSD.h"
2020-10-04 00:25:21 +02:00
# include "Common/File/VFS/VFS.h"
2022-08-27 17:33:37 +02:00
# include "Common/VR/PPSSPPVR.h"
2020-10-04 09:29:36 +02:00
# include "Common/Log.h"
2020-08-15 20:53:08 +02:00
# include "Common/TimeUtil.h"
2020-05-09 22:06:22 -07:00
# include "Core/Config.h"
# include "Core/ConfigValues.h"
# include "Core/System.h"
2022-01-30 10:37:50 -08:00
# include "Core/HW/Display.h"
2020-05-09 22:06:22 -07:00
# include "GPU/Common/PostShader.h"
# include "GPU/Common/PresentationCommon.h"
2022-10-26 16:02:18 +02:00
# include "GPU/GPUState.h"
2020-11-09 09:00:06 +01:00
# include "Common/GPU/ShaderTranslation.h"
2020-05-09 22:06:22 -07:00
struct Vertex {
float x , y , z ;
float u , v ;
uint32_t rgba ;
} ;
2020-07-05 22:46:04 +02:00
FRect GetScreenFrame ( float pixelWidth , float pixelHeight ) {
2020-05-31 20:13:35 +02:00
FRect rc = FRect {
0.0f ,
0.0f ,
pixelWidth ,
pixelHeight ,
} ;
2020-07-05 22:46:04 +02:00
bool applyInset = ! g_Config . bIgnoreScreenInsets ;
2020-05-31 20:13:35 +02:00
2020-07-05 22:46:04 +02:00
if ( applyInset ) {
// Remove the DPI scale to get back to pixels.
2023-02-25 13:09:44 +01:00
float left = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_LEFT ) / g_display . dpi_scale_x ;
float right = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_RIGHT ) / g_display . dpi_scale_x ;
float top = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_TOP ) / g_display . dpi_scale_y ;
float bottom = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_BOTTOM ) / g_display . dpi_scale_y ;
2020-07-05 22:46:04 +02:00
// Adjust left edge to compensate for cutouts (notches) if any.
rc . x + = left ;
rc . w - = ( left + right ) ;
rc . y + = top ;
rc . h - = ( top + bottom ) ;
}
2020-05-31 20:13:35 +02:00
return rc ;
}
2023-04-02 22:27:31 +02:00
void CalculateDisplayOutputRect ( FRect * rc , float origW , float origH , const FRect & frame , int rotation ) {
2020-05-09 22:06:22 -07:00
float outW ;
float outH ;
bool rotated = rotation = = ROTATION_LOCKED_VERTICAL | | rotation = = ROTATION_LOCKED_VERTICAL180 ;
2023-04-02 18:29:57 +02:00
bool stretch = g_Config . bDisplayStretch & & ! g_Config . bDisplayIntegerScale ;
2022-11-27 10:04:26 +01:00
float offsetX = g_Config . fDisplayOffsetX ;
float offsetY = g_Config . fDisplayOffsetY ;
float scale = g_Config . fDisplayScale ;
float aspectRatioAdjust = g_Config . fDisplayAspectRatio ;
2022-10-17 18:36:54 +02:00
2022-11-27 10:04:26 +01:00
float origRatio = ! rotated ? origW / origH : origH / origW ;
float frameRatio = frame . w / frame . h ;
2020-05-09 22:06:22 -07:00
2022-11-27 10:04:26 +01:00
if ( stretch ) {
// Automatically set aspect ratio to match the display, IF the rotation matches the output display ratio! Otherwise, just
// sets standard aspect ratio because actually stretching will just look silly.
2023-02-25 13:09:44 +01:00
bool globalRotated = g_display . rotation = = DisplayRotation : : ROTATE_90 | | g_display . rotation = = DisplayRotation : : ROTATE_270 ;
if ( rotated = = g_display . dp_yres > g_display . dp_xres ) {
2022-11-27 10:04:26 +01:00
origRatio = frameRatio ;
2020-05-09 22:06:22 -07:00
} else {
2022-11-27 10:04:26 +01:00
origRatio * = aspectRatioAdjust ;
2020-05-09 22:06:22 -07:00
}
2022-11-27 10:04:26 +01:00
} else {
origRatio * = aspectRatioAdjust ;
2020-05-09 22:06:22 -07:00
}
2022-11-27 10:04:26 +01:00
float scaledWidth = frame . w * scale ;
float scaledHeight = frame . h * scale ;
if ( origRatio > frameRatio ) {
// Image is wider than frame. Center vertically.
outW = scaledWidth ;
outH = scaledWidth / origRatio ;
} else {
// Image is taller than frame. Center horizontally.
outW = scaledHeight * origRatio ;
outH = scaledHeight ;
}
2024-01-15 21:58:52 +01:00
// Ye olde 1080p hack: If everything is setup to exactly cover the screen (defaults), and the screen display aspect ratio is 16:9,
// cut off one line from the top and bottom.
if ( scale = = 1.0f & & aspectRatioAdjust = = 1.0f & & offsetX = = 0.5f & & offsetY = = 0.5f & & ! g_Config . bDisplayIntegerScale & & g_Config . bDisplayCropTo16x9 ) {
if ( fabsf ( frame . w / frame . h - 16.0f / 9.0f ) < 0.0001f ) {
outW * = 272.0f / 270.0f ;
outH * = 272.0f / 270.0f ;
}
}
2023-04-02 18:29:57 +02:00
if ( g_Config . bDisplayIntegerScale ) {
2023-04-02 22:35:51 +02:00
float wDim = 480.0f ;
if ( rotated ) {
wDim = 272.0f ;
}
2023-04-30 01:26:09 +02:00
int zoom = g_Config . iInternalResolution ;
if ( zoom = = 0 ) {
// Auto (1:1) mode, not super meaningful with integer scaling, but let's do something that makes
// some sense. use the longest dimension, just to have something. round down.
if ( ! g_Config . IsPortrait ( ) ) {
zoom = ( PSP_CoreParameter ( ) . pixelWidth ) / 480 ;
} else {
zoom = ( PSP_CoreParameter ( ) . pixelHeight ) / 480 ;
}
}
2023-04-05 09:48:28 +02:00
// If integer scaling, limit ourselves to even multiples of the rendered resolution,
// to make sure all the pixels are square.
2023-04-30 01:26:09 +02:00
wDim * = zoom ;
2023-04-02 22:35:51 +02:00
outW = std : : max ( 1.0f , floorf ( outW / wDim ) ) * wDim ;
2023-04-02 18:29:57 +02:00
outH = outW / origRatio ;
}
2022-11-28 19:25:13 +01:00
if ( IsVREnabled ( ) ) {
rc - > x = 0 ;
rc - > y = 0 ;
rc - > w = floorf ( frame . w ) ;
rc - > h = floorf ( frame . h ) ;
outW = frame . w ;
outH = frame . h ;
} else {
rc - > x = floorf ( frame . x + frame . w * offsetX - outW * 0.5f ) ;
rc - > y = floorf ( frame . y + frame . h * offsetY - outH * 0.5f ) ;
rc - > w = floorf ( outW ) ;
rc - > h = floorf ( outH ) ;
}
2020-05-09 22:06:22 -07:00
}
PresentationCommon : : PresentationCommon ( Draw : : DrawContext * draw ) : draw_ ( draw ) {
CreateDeviceObjects ( ) ;
}
PresentationCommon : : ~ PresentationCommon ( ) {
DestroyDeviceObjects ( ) ;
}
2022-10-21 15:42:32 +02:00
void PresentationCommon : : GetCardboardSettings ( CardboardSettings * cardboardSettings ) const {
2020-05-31 20:13:35 +02:00
if ( ! g_Config . bEnableCardboardVR ) {
cardboardSettings - > enabled = false ;
return ;
}
2021-08-28 22:28:02 +02:00
2020-05-09 22:06:22 -07:00
// Calculate Cardboard Settings
float cardboardScreenScale = g_Config . iCardboardScreenSize / 100.0f ;
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale ;
2021-08-28 22:28:02 +02:00
float cardboardScreenHeight = pixelHeight_ * cardboardScreenScale ;
2020-05-09 22:06:22 -07:00
float cardboardMaxXShift = ( pixelWidth_ / 2.0f - cardboardScreenWidth ) / 2.0f ;
float cardboardUserXShift = g_Config . iCardboardXShift / 100.0f * cardboardMaxXShift ;
float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift ;
float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift ;
float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f ;
float cardboardUserYShift = g_Config . iCardboardYShift / 100.0f * cardboardMaxYShift ;
float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift ;
2020-05-31 20:13:35 +02:00
cardboardSettings - > enabled = true ;
2020-05-09 22:06:22 -07:00
cardboardSettings - > leftEyeXPosition = cardboardLeftEyeX ;
cardboardSettings - > rightEyeXPosition = cardboardRightEyeX ;
cardboardSettings - > screenYPosition = cardboardScreenY ;
cardboardSettings - > screenWidth = cardboardScreenWidth ;
cardboardSettings - > screenHeight = cardboardScreenHeight ;
}
2022-12-12 19:09:14 -08:00
static float GetShaderSettingValue ( const ShaderInfo * shaderInfo , int i , const char * nameSuffix ) {
std : : string key = shaderInfo - > section + nameSuffix ;
auto it = g_Config . mPostShaderSetting . find ( key ) ;
if ( it ! = g_Config . mPostShaderSetting . end ( ) )
return it - > second ;
return shaderInfo - > settings [ i ] . value ;
}
2022-10-21 15:42:32 +02:00
void PresentationCommon : : CalculatePostShaderUniforms ( int bufferWidth , int bufferHeight , int targetWidth , int targetHeight , const ShaderInfo * shaderInfo , PostShaderUniforms * uniforms ) const {
2020-05-09 22:06:22 -07:00
float u_delta = 1.0f / bufferWidth ;
float v_delta = 1.0f / bufferHeight ;
2020-05-15 23:41:13 -07:00
float u_pixel_delta = 1.0f / targetWidth ;
float v_pixel_delta = 1.0f / targetHeight ;
2020-05-09 22:06:22 -07:00
int flipCount = __DisplayGetFlipCount ( ) ;
int vCount = __DisplayGetVCount ( ) ;
2020-08-16 00:13:19 +02:00
float time [ 4 ] = { ( float ) time_now_d ( ) , ( vCount % 60 ) * 1.0f / 60.0f , ( float ) vCount , ( float ) ( flipCount % 60 ) } ;
2020-05-09 22:06:22 -07:00
uniforms - > texelDelta [ 0 ] = u_delta ;
uniforms - > texelDelta [ 1 ] = v_delta ;
uniforms - > pixelDelta [ 0 ] = u_pixel_delta ;
uniforms - > pixelDelta [ 1 ] = v_pixel_delta ;
memcpy ( uniforms - > time , time , 4 * sizeof ( float ) ) ;
2021-06-12 11:27:26 -07:00
uniforms - > timeDelta [ 0 ] = time [ 0 ] - previousUniforms_ . time [ 0 ] ;
uniforms - > timeDelta [ 1 ] = ( time [ 2 ] - previousUniforms_ . time [ 2 ] ) * ( 1.0f / 60.0f ) ;
uniforms - > timeDelta [ 2 ] = time [ 2 ] - previousUniforms_ . time [ 2 ] ;
uniforms - > timeDelta [ 3 ] = time [ 3 ] ! = previousUniforms_ . time [ 3 ] ? 1.0f : 0.0f ;
2020-05-15 23:41:13 -07:00
uniforms - > video = hasVideo_ ? 1.0f : 0.0f ;
2020-05-10 16:53:15 -07:00
// The shader translator tacks this onto our shaders, if we don't set it they render garbage.
uniforms - > gl_HalfPixel [ 0 ] = u_pixel_delta * 0.5f ;
uniforms - > gl_HalfPixel [ 1 ] = v_pixel_delta * 0.5f ;
2020-05-15 11:15:38 +02:00
2022-12-12 19:11:20 -08:00
uniforms - > setting [ 0 ] = GetShaderSettingValue ( shaderInfo , 0 , " SettingCurrentValue1 " ) ;
uniforms - > setting [ 1 ] = GetShaderSettingValue ( shaderInfo , 1 , " SettingCurrentValue2 " ) ;
uniforms - > setting [ 2 ] = GetShaderSettingValue ( shaderInfo , 2 , " SettingCurrentValue3 " ) ;
uniforms - > setting [ 3 ] = GetShaderSettingValue ( shaderInfo , 3 , " SettingCurrentValue4 " ) ;
2020-05-10 16:53:15 -07:00
}
2021-05-09 15:25:12 +02:00
static std : : string ReadShaderSrc ( const Path & filename ) {
2020-05-10 16:53:15 -07:00
size_t sz = 0 ;
2023-03-06 14:23:56 +01:00
char * data = ( char * ) g_VFS . ReadFile ( filename . c_str ( ) , & sz ) ;
2021-05-09 15:25:12 +02:00
if ( ! data ) {
2020-05-10 16:53:15 -07:00
return " " ;
2021-05-09 15:25:12 +02:00
}
2020-05-10 16:53:15 -07:00
std : : string src ( data , sz ) ;
2021-01-01 15:40:12 -08:00
delete [ ] data ;
2020-05-10 16:53:15 -07:00
return src ;
}
// Note: called on resize and settings changes.
2022-10-21 12:35:49 +02:00
// Also takes care of making sure the appropriate stereo shader is compiled.
2020-05-10 16:53:15 -07:00
bool PresentationCommon : : UpdatePostShader ( ) {
2022-10-21 12:35:49 +02:00
DestroyStereoShader ( ) ;
2022-10-26 16:02:18 +02:00
if ( gstate_c . Use ( GPU_USE_SIMPLE_STEREO_PERSPECTIVE ) ) {
2022-10-21 12:35:49 +02:00
const ShaderInfo * stereoShaderInfo = GetPostShaderInfo ( g_Config . sStereoToMonoShader ) ;
2022-10-28 10:17:29 +02:00
if ( stereoShaderInfo ) {
bool result = CompilePostShader ( stereoShaderInfo , & stereoPipeline_ ) ;
2022-10-28 17:42:21 +02:00
if ( result ) {
2022-10-28 10:17:29 +02:00
stereoShaderInfo_ = new ShaderInfo ( * stereoShaderInfo ) ;
}
} else {
2022-10-28 17:42:21 +02:00
WARN_LOG ( G3D , " Failed to get info about stereo shader '%s' " , g_Config . sStereoToMonoShader . c_str ( ) ) ;
2022-10-21 12:35:49 +02:00
}
}
2020-05-16 00:03:39 -07:00
std : : vector < const ShaderInfo * > shaderInfo ;
2021-04-10 18:35:23 -07:00
if ( ! g_Config . vPostShaderNames . empty ( ) ) {
2021-09-28 23:35:57 +02:00
ReloadAllPostShaderInfo ( draw_ ) ;
2020-05-18 11:17:45 +02:00
shaderInfo = GetFullPostShadersChain ( g_Config . vPostShaderNames ) ;
2020-05-10 16:53:15 -07:00
}
DestroyPostShader ( ) ;
2022-10-21 15:42:32 +02:00
if ( shaderInfo . empty ( ) ) {
usePostShader_ = false ;
2020-05-10 16:53:15 -07:00
return false ;
2022-10-21 15:42:32 +02:00
}
2020-05-10 16:53:15 -07:00
2021-06-12 10:54:36 -07:00
bool usePreviousFrame = false ;
bool usePreviousAtOutputResolution = false ;
2022-03-13 12:03:48 -07:00
for ( size_t i = 0 ; i < shaderInfo . size ( ) ; + + i ) {
2020-05-16 00:03:39 -07:00
const ShaderInfo * next = i + 1 < shaderInfo . size ( ) ? shaderInfo [ i + 1 ] : nullptr ;
2022-10-21 15:42:32 +02:00
Draw : : Pipeline * postPipeline = nullptr ;
if ( ! BuildPostShader ( shaderInfo [ i ] , next , & postPipeline ) ) {
2020-05-16 00:03:39 -07:00
DestroyPostShader ( ) ;
return false ;
}
2022-10-21 15:42:32 +02:00
_dbg_assert_ ( postPipeline ) ;
postShaderPipelines_ . push_back ( postPipeline ) ;
postShaderInfo_ . push_back ( * shaderInfo [ i ] ) ;
2021-06-12 10:54:36 -07:00
if ( shaderInfo [ i ] - > usePreviousFrame ) {
usePreviousFrame = true ;
usePreviousAtOutputResolution = shaderInfo [ i ] - > outputResolution ;
}
}
if ( usePreviousFrame ) {
int w = usePreviousAtOutputResolution ? pixelWidth_ : renderWidth_ ;
int h = usePreviousAtOutputResolution ? pixelHeight_ : renderHeight_ ;
2023-05-26 10:28:10 +02:00
_dbg_assert_ ( w > 0 & & h > 0 ) ;
2021-06-12 10:54:36 -07:00
static constexpr int FRAMES = 2 ;
previousFramebuffers_ . resize ( FRAMES ) ;
previousIndex_ = 0 ;
for ( int i = 0 ; i < FRAMES ; + + i ) {
2022-11-28 18:20:30 +01:00
previousFramebuffers_ [ i ] = draw_ - > CreateFramebuffer ( { w , h , 1 , 1 , 0 , false , " inter_presentation " } ) ;
2021-06-12 10:54:36 -07:00
if ( ! previousFramebuffers_ [ i ] ) {
DestroyPostShader ( ) ;
return false ;
}
}
2020-05-15 22:24:55 -07:00
}
usePostShader_ = true ;
return true ;
}
2022-10-21 15:42:32 +02:00
bool PresentationCommon : : CompilePostShader ( const ShaderInfo * shaderInfo , Draw : : Pipeline * * outPipeline ) const {
2022-10-28 10:17:29 +02:00
_assert_ ( shaderInfo ) ;
2020-05-10 16:53:15 -07:00
std : : string vsSourceGLSL = ReadShaderSrc ( shaderInfo - > vertexShaderFile ) ;
std : : string fsSourceGLSL = ReadShaderSrc ( shaderInfo - > fragmentShaderFile ) ;
if ( vsSourceGLSL . empty ( ) | | fsSourceGLSL . empty ( ) ) {
return false ;
}
2021-01-04 23:51:34 +01:00
std : : string vsError ;
std : : string fsError ;
2021-01-03 19:13:25 +01:00
// All post shaders are written in GLSL 1.0 so that's what we pass in here as a "from" language.
2020-11-05 08:34:35 +01:00
Draw : : ShaderModule * vs = CompileShaderModule ( ShaderStage : : Vertex , GLSL_1xx , vsSourceGLSL , & vsError ) ;
Draw : : ShaderModule * fs = CompileShaderModule ( ShaderStage : : Fragment , GLSL_1xx , fsSourceGLSL , & fsError ) ;
2020-05-10 16:53:15 -07:00
// Don't worry, CompileShaderModule makes sure they get freed if one succeeded.
if ( ! fs | | ! vs ) {
std : : string errorString = vsError + " \n " + fsError ;
2021-01-04 23:51:34 +01:00
// DO NOT turn this into an ERROR_LOG_REPORT, as it will pollute our logs with all kinds of
2020-05-10 16:53:15 -07:00
// user shader experiments.
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 ( ) ) ;
ShowPostShaderError ( errorString ) ;
return false ;
}
2020-11-03 15:44:57 +01:00
UniformBufferDesc postShaderDesc { sizeof ( PostShaderUniforms ) , {
{ " gl_HalfPixel " , 0 , - 1 , UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , gl_HalfPixel ) } ,
{ " u_texelDelta " , 1 , 1 , UniformType : : FLOAT2 , offsetof ( PostShaderUniforms , texelDelta ) } ,
{ " u_pixelDelta " , 2 , 2 , UniformType : : FLOAT2 , offsetof ( PostShaderUniforms , pixelDelta ) } ,
{ " u_time " , 3 , 3 , UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , time ) } ,
2021-06-12 11:27:26 -07:00
{ " u_timeDelta " , 4 , 4 , UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , timeDelta ) } ,
{ " u_setting " , 5 , 5 , UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , setting ) } ,
{ " u_video " , 6 , 6 , UniformType : : FLOAT1 , offsetof ( PostShaderUniforms , video ) } ,
2020-05-10 16:53:15 -07:00
} } ;
2021-01-04 23:51:34 +01:00
2020-05-10 16:53:15 -07:00
Draw : : Pipeline * pipeline = CreatePipeline ( { vs , fs } , true , & postShaderDesc ) ;
2022-10-21 14:18:18 +02:00
fs - > Release ( ) ;
vs - > Release ( ) ;
2020-05-10 16:53:15 -07:00
if ( ! pipeline )
return false ;
2022-10-21 15:42:32 +02:00
* outPipeline = pipeline ;
return true ;
}
bool PresentationCommon : : BuildPostShader ( const ShaderInfo * shaderInfo , const ShaderInfo * next , Draw : : Pipeline * * outPipeline ) {
if ( ! CompilePostShader ( shaderInfo , outPipeline ) ) {
return false ;
}
2020-05-15 22:24:55 -07:00
if ( ! shaderInfo - > outputResolution | | next ) {
int nextWidth = renderWidth_ ;
int nextHeight = renderHeight_ ;
2020-05-16 10:21:00 -07:00
// When chaining, we use the previous resolution as a base, rather than the render resolution.
if ( ! postShaderFramebuffers_ . empty ( ) )
draw_ - > GetFramebufferDimensions ( postShaderFramebuffers_ . back ( ) , & nextWidth , & nextHeight ) ;
2020-05-15 22:24:55 -07:00
if ( next & & next - > isUpscalingFilter ) {
// Force 1x for this shader, so the next can upscale.
const bool isPortrait = g_Config . IsPortrait ( ) ;
nextWidth = isPortrait ? 272 : 480 ;
nextHeight = isPortrait ? 480 : 272 ;
} else if ( next & & next - > SSAAFilterLevel > = 2 ) {
// Increase the resolution this shader outputs for the next to SSAA.
nextWidth * = next - > SSAAFilterLevel ;
nextHeight * = next - > SSAAFilterLevel ;
} else if ( shaderInfo - > outputResolution ) {
// If the current shader uses output res (not next), we will use output res for it.
2020-05-31 19:45:28 +02:00
FRect rc ;
2020-07-05 22:46:04 +02:00
FRect frame = GetScreenFrame ( ( float ) pixelWidth_ , ( float ) pixelHeight_ ) ;
2023-04-02 22:27:31 +02:00
CalculateDisplayOutputRect ( & rc , 480.0f , 272.0f , frame , g_Config . iInternalScreenRotation ) ;
2020-05-31 19:45:28 +02:00
nextWidth = ( int ) rc . w ;
nextHeight = ( int ) rc . h ;
2020-05-15 22:24:55 -07:00
}
2020-05-16 10:02:12 -07:00
if ( ! AllocateFramebuffer ( nextWidth , nextHeight ) ) {
2022-10-21 15:42:32 +02:00
( * outPipeline ) - > Release ( ) ;
* outPipeline = nullptr ;
2020-05-15 22:24:55 -07:00
return false ;
}
}
2020-05-10 16:53:15 -07:00
return true ;
}
2020-05-16 10:02:12 -07:00
bool PresentationCommon : : AllocateFramebuffer ( int w , int h ) {
using namespace Draw ;
// First, let's try to find a framebuffer of the right size that is NOT the most recent.
Framebuffer * last = postShaderFramebuffers_ . empty ( ) ? nullptr : postShaderFramebuffers_ . back ( ) ;
for ( const auto & prev : postShaderFBOUsage_ ) {
if ( prev . w = = w & & prev . h = = h & & prev . fbo ! = last ) {
// Great, this one's perfect. Ref it for when we release.
prev . fbo - > AddRef ( ) ;
postShaderFramebuffers_ . push_back ( prev . fbo ) ;
return true ;
}
}
// No depth/stencil for post processing
2022-11-28 18:20:30 +01:00
Draw : : Framebuffer * fbo = draw_ - > CreateFramebuffer ( { w , h , 1 , 1 , 0 , false , " presentation " } ) ;
2020-05-16 10:02:12 -07:00
if ( ! fbo ) {
return false ;
}
postShaderFBOUsage_ . push_back ( { fbo , w , h } ) ;
postShaderFramebuffers_ . push_back ( fbo ) ;
return true ;
}
2020-05-10 16:53:15 -07:00
void PresentationCommon : : ShowPostShaderError ( const std : : string & errorString ) {
// 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 ' & & i = = start ) {
start = i + 1 ;
} else if ( errorString [ i ] = = ' \n ' ) {
firstLine = errorString . substr ( start , i - start ) ;
if ( blacklistedLines . find ( firstLine ) = = blacklistedLines . end ( ) ) {
break ;
}
start = i + 1 ;
firstLine . clear ( ) ;
}
}
if ( ! firstLine . empty ( ) ) {
2023-06-20 14:40:46 +02:00
g_OSD . Show ( OSDType : : MESSAGE_ERROR_DUMP , " Post-shader error: " + firstLine + " ...: \n " + errorString , 10.0f ) ;
2020-05-10 16:53:15 -07:00
} else {
2023-06-20 14:40:46 +02:00
g_OSD . Show ( OSDType : : MESSAGE_ERROR , " Post-shader error, see log for details " , 10.0f ) ;
2020-05-10 16:53:15 -07:00
}
2020-05-09 22:06:22 -07:00
}
void PresentationCommon : : DeviceLost ( ) {
DestroyDeviceObjects ( ) ;
2022-08-23 20:15:30 -07:00
draw_ = nullptr ;
2020-05-09 22:06:22 -07:00
}
void PresentationCommon : : DeviceRestore ( Draw : : DrawContext * draw ) {
draw_ = draw ;
CreateDeviceObjects ( ) ;
}
2022-10-21 15:42:32 +02:00
Draw : : Pipeline * PresentationCommon : : CreatePipeline ( std : : vector < Draw : : ShaderModule * > shaders , bool postShader , const UniformBufferDesc * uniformDesc ) const {
2020-05-09 22:06:22 -07:00
using namespace Draw ;
2020-05-10 16:53:15 -07:00
Semantic pos = SEM_POSITION ;
Semantic tc = SEM_TEXCOORD0 ;
// Shader translation marks these both as "TEXCOORDs" on HLSL...
2020-10-30 10:22:40 +01:00
if ( postShader & & ( lang_ = = HLSL_D3D11 | | lang_ = = HLSL_D3D9 ) ) {
2020-05-10 16:53:15 -07:00
pos = SEM_TEXCOORD0 ;
tc = SEM_TEXCOORD1 ;
}
2020-05-09 22:06:22 -07:00
// TODO: Maybe get rid of color0.
InputLayoutDesc inputDesc = {
2023-05-17 17:22:50 +02:00
sizeof ( Vertex ) ,
2020-05-09 22:06:22 -07:00
{
2023-05-17 17:22:50 +02:00
{ pos , DataFormat : : R32G32B32_FLOAT , 0 } ,
{ tc , DataFormat : : R32G32_FLOAT , 12 } ,
{ SEM_COLOR0 , DataFormat : : R8G8B8A8_UNORM , 20 } ,
2020-05-09 22:06:22 -07:00
} ,
} ;
InputLayout * inputLayout = draw_ - > CreateInputLayout ( inputDesc ) ;
DepthStencilState * depth = draw_ - > CreateDepthStencilState ( { false , false , Comparison : : LESS } ) ;
BlendState * blendstateOff = draw_ - > CreateBlendState ( { false , 0xF } ) ;
RasterState * rasterNoCull = draw_ - > CreateRasterState ( { } ) ;
2023-04-08 13:02:18 +02:00
PipelineDesc pipelineDesc { Primitive : : TRIANGLE_STRIP , shaders , inputLayout , depth , blendstateOff , rasterNoCull , uniformDesc } ;
2022-09-07 23:55:33 +02:00
Pipeline * pipeline = draw_ - > CreateGraphicsPipeline ( pipelineDesc , " presentation " ) ;
2020-05-09 22:06:22 -07:00
inputLayout - > Release ( ) ;
depth - > Release ( ) ;
blendstateOff - > Release ( ) ;
rasterNoCull - > Release ( ) ;
2020-05-10 16:53:15 -07:00
return pipeline ;
}
void PresentationCommon : : CreateDeviceObjects ( ) {
using namespace Draw ;
2020-08-16 00:38:55 +02:00
_assert_ ( vdata_ = = nullptr ) ;
2020-05-10 16:53:15 -07:00
2023-04-08 12:53:41 +02:00
// TODO: Could probably just switch to DrawUP, it's supported well by all backends now.
vdata_ = draw_ - > CreateBuffer ( sizeof ( Vertex ) * 12 , BufferUsageFlag : : DYNAMIC | BufferUsageFlag : : VERTEXDATA ) ;
2020-05-10 16:53:15 -07:00
2020-05-13 08:42:43 -07:00
samplerNearest_ = draw_ - > CreateSamplerState ( { TextureFilter : : NEAREST , TextureFilter : : NEAREST , TextureFilter : : NEAREST , 0.0f , TextureAddressMode : : CLAMP_TO_EDGE , TextureAddressMode : : CLAMP_TO_EDGE , TextureAddressMode : : CLAMP_TO_EDGE } ) ;
samplerLinear_ = draw_ - > CreateSamplerState ( { TextureFilter : : LINEAR , TextureFilter : : LINEAR , TextureFilter : : LINEAR , 0.0f , TextureAddressMode : : CLAMP_TO_EDGE , TextureAddressMode : : CLAMP_TO_EDGE , TextureAddressMode : : CLAMP_TO_EDGE } ) ;
2020-05-10 16:53:15 -07:00
texColor_ = CreatePipeline ( { draw_ - > GetVshaderPreset ( VS_TEXTURE_COLOR_2D ) , draw_ - > GetFshaderPreset ( FS_TEXTURE_COLOR_2D ) } , false , & vsTexColBufDesc ) ;
texColorRBSwizzle_ = CreatePipeline ( { draw_ - > GetVshaderPreset ( VS_TEXTURE_COLOR_2D ) , draw_ - > GetFshaderPreset ( FS_TEXTURE_COLOR_2D_RB_SWIZZLE ) } , false , & vsTexColBufDesc ) ;
if ( restorePostShader_ )
UpdatePostShader ( ) ;
restorePostShader_ = false ;
2020-05-09 22:06:22 -07:00
}
template < typename T >
static void DoRelease ( T * & obj ) {
if ( obj )
obj - > Release ( ) ;
obj = nullptr ;
}
2020-05-10 16:53:15 -07:00
template < typename T >
static void DoReleaseVector ( std : : vector < T * > & list ) {
for ( auto & obj : list )
obj - > Release ( ) ;
list . clear ( ) ;
}
2020-05-09 22:06:22 -07:00
void PresentationCommon : : DestroyDeviceObjects ( ) {
DoRelease ( texColor_ ) ;
DoRelease ( texColorRBSwizzle_ ) ;
DoRelease ( samplerNearest_ ) ;
DoRelease ( samplerLinear_ ) ;
DoRelease ( vdata_ ) ;
2020-05-10 00:38:19 -07:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
2020-05-10 16:53:15 -07:00
2020-05-15 22:24:55 -07:00
restorePostShader_ = usePostShader_ ;
2020-05-10 16:53:15 -07:00
DestroyPostShader ( ) ;
2022-10-21 12:35:49 +02:00
DestroyStereoShader ( ) ;
2020-05-10 16:53:15 -07:00
}
void PresentationCommon : : DestroyPostShader ( ) {
usePostShader_ = false ;
DoReleaseVector ( postShaderPipelines_ ) ;
DoReleaseVector ( postShaderFramebuffers_ ) ;
2021-06-12 10:54:36 -07:00
DoReleaseVector ( previousFramebuffers_ ) ;
2020-05-15 22:24:55 -07:00
postShaderInfo_ . clear ( ) ;
2020-05-16 10:02:12 -07:00
postShaderFBOUsage_ . clear ( ) ;
2020-05-10 16:53:15 -07:00
}
2022-10-21 12:35:49 +02:00
void PresentationCommon : : DestroyStereoShader ( ) {
DoRelease ( stereoPipeline_ ) ;
2022-10-22 17:34:23 +02:00
delete stereoShaderInfo_ ;
stereoShaderInfo_ = nullptr ;
2022-10-21 12:35:49 +02:00
}
2022-10-21 15:42:32 +02:00
Draw : : ShaderModule * PresentationCommon : : CompileShaderModule ( ShaderStage stage , ShaderLanguage lang , const std : : string & src , std : : string * errorString ) const {
2020-05-10 16:53:15 -07:00
std : : string translated = src ;
if ( lang ! = lang_ ) {
// Gonna have to upconvert the shader.
2020-12-19 23:45:31 +01:00
if ( ! TranslateShader ( & translated , lang_ , draw_ - > GetShaderLanguageDesc ( ) , nullptr , src , lang , stage , errorString ) ) {
2021-01-03 19:13:25 +01:00
ERROR_LOG ( FRAMEBUF , " Failed to translate post-shader. Error string: '%s' \n Source code: \n %s \n " , errorString - > c_str ( ) , src . c_str ( ) ) ;
2020-05-10 16:53:15 -07:00
return nullptr ;
}
}
2020-11-04 09:40:11 +01:00
Draw : : ShaderModule * shader = draw_ - > CreateShaderModule ( stage , lang_ , ( const uint8_t * ) translated . c_str ( ) , translated . size ( ) , " postshader " ) ;
2020-05-10 16:53:15 -07:00
return shader ;
2020-05-09 22:06:22 -07:00
}
2020-05-15 23:41:13 -07:00
void PresentationCommon : : SourceTexture ( Draw : : Texture * texture , int bufferWidth , int bufferHeight ) {
2023-05-30 13:08:51 +02:00
// AddRef before release and assign in case it's the same.
texture - > AddRef ( ) ;
2020-05-10 00:38:19 -07:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
srcTexture_ = texture ;
2020-05-15 23:41:13 -07:00
srcWidth_ = bufferWidth ;
srcHeight_ = bufferHeight ;
2020-05-10 00:38:19 -07:00
}
2020-05-15 23:41:13 -07:00
void PresentationCommon : : SourceFramebuffer ( Draw : : Framebuffer * fb , int bufferWidth , int bufferHeight ) {
2023-05-30 13:08:51 +02:00
fb - > AddRef ( ) ;
2020-05-10 00:38:19 -07:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
srcFramebuffer_ = fb ;
2020-05-15 23:41:13 -07:00
srcWidth_ = bufferWidth ;
srcHeight_ = bufferHeight ;
2020-05-10 00:38:19 -07:00
}
2022-10-21 12:35:49 +02:00
// Return value is if stereo binding succeeded.
bool PresentationCommon : : BindSource ( int binding , bool bindStereo ) {
2020-05-10 16:53:15 -07:00
if ( srcTexture_ ) {
2020-05-17 10:47:39 -07:00
draw_ - > BindTexture ( binding , srcTexture_ ) ;
2022-10-21 12:35:49 +02:00
return false ;
2020-05-10 16:53:15 -07:00
} else if ( srcFramebuffer_ ) {
2022-10-21 12:35:49 +02:00
if ( bindStereo ) {
if ( srcFramebuffer_ - > Layers ( ) > 1 ) {
draw_ - > BindFramebufferAsTexture ( srcFramebuffer_ , binding , Draw : : FB_COLOR_BIT , Draw : : ALL_LAYERS ) ;
return true ;
} else {
2022-10-25 23:59:42 +02:00
// Single layer. This might be from a post shader and those don't yet support stereo.
2022-10-21 12:35:49 +02:00
draw_ - > BindFramebufferAsTexture ( srcFramebuffer_ , binding , Draw : : FB_COLOR_BIT , 0 ) ;
return false ;
}
} else {
draw_ - > BindFramebufferAsTexture ( srcFramebuffer_ , binding , Draw : : FB_COLOR_BIT , 0 ) ;
return false ;
}
2020-05-10 16:53:15 -07:00
} else {
2020-08-16 00:38:55 +02:00
_assert_ ( false ) ;
2022-10-21 12:35:49 +02:00
return false ;
2020-05-10 16:53:15 -07:00
}
}
2020-05-15 23:41:13 -07:00
void PresentationCommon : : UpdateUniforms ( bool hasVideo ) {
hasVideo_ = hasVideo ;
}
void PresentationCommon : : CopyToOutput ( OutputFlags flags , int uvRotation , float u0 , float v0 , float u1 , float v1 ) {
2022-12-01 19:15:38 +01:00
draw_ - > Invalidate ( InvalidationFlags : : CACHED_RENDER_STATE ) ;
2020-05-10 16:53:15 -07:00
// TODO: If shader objects have been created by now, we might have received errors.
// GLES can have the shader fail later, shader->failed / shader->error.
// This should auto-disable usePostShader_ and call ShowPostShaderError().
bool useNearest = flags & OutputFlags : : NEAREST ;
2022-10-26 16:02:18 +02:00
bool useStereo = gstate_c . Use ( GPU_USE_SIMPLE_STEREO_PERSPECTIVE ) & & stereoPipeline_ ! = nullptr ; // TODO: Also check that the backend has support for it.
2022-10-21 12:35:49 +02:00
const bool usePostShader = usePostShader_ & & ! useStereo & & ! ( flags & OutputFlags : : RB_SWIZZLE ) ;
2020-05-15 22:24:55 -07:00
const bool isFinalAtOutputResolution = usePostShader & & postShaderFramebuffers_ . size ( ) < postShaderPipelines_ . size ( ) ;
2021-06-12 10:54:36 -07:00
Draw : : Framebuffer * postShaderOutput = nullptr ;
2020-05-15 23:41:13 -07:00
int lastWidth = srcWidth_ ;
int lastHeight = srcHeight_ ;
2020-05-10 16:53:15 -07:00
2021-08-28 22:28:02 +02:00
int pixelWidth = pixelWidth_ ;
int pixelHeight = pixelHeight_ ;
2020-05-10 00:38:19 -07:00
// These are the output coordinates.
2021-08-28 22:28:02 +02:00
FRect frame = GetScreenFrame ( ( float ) pixelWidth , ( float ) pixelHeight ) ;
// Note: In cardboard mode, we halve the width here to compensate
// for splitting the window in half, while still reusing normal centering.
if ( g_Config . bEnableCardboardVR ) {
frame . w / = 2.0 ;
pixelWidth / = 2 ;
}
2020-05-31 19:45:28 +02:00
FRect rc ;
2023-04-02 22:27:31 +02:00
CalculateDisplayOutputRect ( & rc , 480.0f , 272.0f , frame , uvRotation ) ;
2020-05-10 00:38:19 -07:00
if ( GetGPUBackend ( ) = = GPUBackend : : DIRECT3D9 ) {
2020-05-31 19:45:28 +02:00
rc . x - = 0.5f ;
2020-05-10 16:53:15 -07:00
// This is plus because the top is larger y.
2020-05-31 19:45:28 +02:00
rc . y + = 0.5f ;
2020-05-10 00:38:19 -07:00
}
2020-05-10 17:40:08 -07:00
// To make buffer updates easier, we use one array of verts.
int postVertsOffset = ( int ) sizeof ( Vertex ) * 4 ;
2023-04-08 12:53:41 +02:00
float finalU0 = u0 , finalU1 = u1 , finalV0 = v0 , finalV1 = v1 ;
2023-05-25 23:53:08 +02:00
if ( usePostShader & & ! ( isFinalAtOutputResolution & & postShaderPipelines_ . size ( ) = = 1 ) ) {
2023-04-08 12:53:41 +02:00
// The final blit will thus use the full texture.
finalU0 = 0.0f ;
finalV0 = 0.0f ;
finalU1 = 1.0f ;
finalV1 = 1.0f ;
}
// Our vertex buffer is split into three parts, with four vertices each:
// 0-3: The final blit vertices (needs to handle cropping the input ONLY if post-processing is not enabled)
// 4-7: Post-processing, other passes
// 8-11: Post-processing, first pass (needs to handle cropping the input image, if wrong dimensions)
Vertex verts [ 12 ] = {
{ rc . x , rc . y , 0 , finalU0 , finalV0 , 0xFFFFFFFF } , // TL
{ rc . x + rc . w , rc . y , 0 , finalU1 , finalV0 , 0xFFFFFFFF } , // TR
{ rc . x , rc . y + rc . h , 0 , finalU0 , finalV1 , 0xFFFFFFFF } , // BL
{ rc . x + rc . w , rc . y + rc . h , 0 , finalU1 , finalV1 , 0xFFFFFFFF } , // BR
2020-05-09 22:06:22 -07:00
} ;
2020-05-10 00:38:19 -07:00
2023-04-08 12:53:41 +02:00
// Rescale X, Y to normalized coordinate system.
2021-08-28 22:28:02 +02:00
float invDestW = 2.0f / pixelWidth ;
float invDestH = 2.0f / pixelHeight ;
2020-05-10 00:38:19 -07:00
for ( int i = 0 ; i < 4 ; i + + ) {
verts [ i ] . x = verts [ i ] . x * invDestW - 1.0f ;
verts [ i ] . y = verts [ i ] . y * invDestH - 1.0f ;
}
if ( uvRotation ! = ROTATION_LOCKED_HORIZONTAL ) {
struct {
float u ;
float v ;
} temp [ 4 ] ;
int rotation = 0 ;
// Vertical and Vertical180 needed swapping after we changed the coordinate system.
switch ( uvRotation ) {
case ROTATION_LOCKED_HORIZONTAL180 : rotation = 2 ; break ;
2020-05-11 23:28:50 -07:00
case ROTATION_LOCKED_VERTICAL : rotation = 3 ; break ;
case ROTATION_LOCKED_VERTICAL180 : rotation = 1 ; break ;
2020-05-10 00:38:19 -07:00
}
2020-05-11 23:28:50 -07:00
// If we flipped, we rotate the other way.
if ( ( flags & OutputFlags : : BACKBUFFER_FLIPPED ) | | ( flags & OutputFlags : : POSITION_FLIPPED ) ) {
if ( ( rotation & 1 ) ! = 0 )
rotation ^ = 2 ;
2020-05-10 00:38:19 -07:00
}
2023-04-08 13:02:18 +02:00
static int rotLookup [ 4 ] = { 0 , 1 , 3 , 2 } ;
2020-05-10 00:38:19 -07:00
for ( int i = 0 ; i < 4 ; i + + ) {
2023-04-08 13:02:18 +02:00
int otherI = rotLookup [ ( rotLookup [ i ] + rotation ) & 3 ] ;
temp [ i ] . u = verts [ otherI ] . u ;
temp [ i ] . v = verts [ otherI ] . v ;
2020-05-10 00:38:19 -07:00
}
for ( int i = 0 ; i < 4 ; i + + ) {
verts [ i ] . u = temp [ i ] . u ;
verts [ i ] . v = temp [ i ] . v ;
}
}
2022-10-23 21:50:00 +02:00
if ( isFinalAtOutputResolution | | useStereo ) {
2020-05-10 17:40:08 -07:00
// In this mode, we ignore the g_display_rot_matrix. Apply manually.
2023-02-25 13:09:44 +01:00
if ( g_display . rotation ! = DisplayRotation : : ROTATE_0 ) {
2020-05-10 17:40:08 -07:00
for ( int i = 0 ; i < 4 ; i + + ) {
Lin : : Vec3 v ( verts [ i ] . x , verts [ i ] . y , verts [ i ] . z ) ;
// Backwards notation, should fix that...
2023-02-25 13:09:44 +01:00
v = v * g_display . rot_matrix ;
2020-05-10 17:40:08 -07:00
verts [ i ] . x = v . x ;
verts [ i ] . y = v . y ;
}
}
}
2020-05-09 22:06:22 -07:00
2020-05-24 16:51:37 +02:00
if ( flags & OutputFlags : : PILLARBOX ) {
for ( int i = 0 ; i < 4 ; i + + ) {
// Looks about right.
verts [ i ] . x * = 0.75f ;
}
}
2023-04-09 23:27:22 +02:00
// Finally, we compensate the y vertex positions for the backbuffer for any flipping.
if ( ( flags & OutputFlags : : POSITION_FLIPPED ) | | ( flags & OutputFlags : : BACKBUFFER_FLIPPED ) ) {
for ( int i = 0 ; i < 4 ; i + + ) {
verts [ i ] . y = - verts [ i ] . y ;
}
}
2021-06-12 10:54:36 -07:00
// Grab the previous framebuffer early so we can change previousIndex_ when we want.
Draw : : Framebuffer * previousFramebuffer = previousFramebuffers_ . empty ( ) ? nullptr : previousFramebuffers_ [ previousIndex_ ] ;
2021-06-12 11:27:26 -07:00
PostShaderUniforms uniforms ;
2023-04-08 12:53:41 +02:00
const auto performShaderPass = [ & ] ( const ShaderInfo * shaderInfo , Draw : : Framebuffer * postShaderFramebuffer , Draw : : Pipeline * postShaderPipeline , int vertsOffset ) {
2021-06-12 10:54:36 -07:00
if ( postShaderOutput ) {
2022-10-18 00:26:10 +02:00
draw_ - > BindFramebufferAsTexture ( postShaderOutput , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
2021-06-12 10:54:36 -07:00
} else {
2022-10-21 12:35:49 +02:00
BindSource ( 0 , false ) ;
2021-06-12 10:54:36 -07:00
}
2022-10-21 12:35:49 +02:00
BindSource ( 1 , false ) ;
2021-06-12 10:54:36 -07:00
if ( shaderInfo - > usePreviousFrame )
2022-10-18 00:26:10 +02:00
draw_ - > BindFramebufferAsTexture ( previousFramebuffer , 2 , Draw : : FB_COLOR_BIT , 0 ) ;
2021-06-12 10:54:36 -07:00
int nextWidth , nextHeight ;
draw_ - > GetFramebufferDimensions ( postShaderFramebuffer , & nextWidth , & nextHeight ) ;
Draw : : Viewport viewport { 0 , 0 , ( float ) nextWidth , ( float ) nextHeight , 0.0f , 1.0f } ;
2023-02-24 22:09:32 +01:00
draw_ - > SetViewport ( viewport ) ;
2021-06-12 10:54:36 -07:00
draw_ - > SetScissorRect ( 0 , 0 , nextWidth , nextHeight ) ;
CalculatePostShaderUniforms ( lastWidth , lastHeight , nextWidth , nextHeight , shaderInfo , & uniforms ) ;
draw_ - > BindPipeline ( postShaderPipeline ) ;
draw_ - > UpdateDynamicUniformBuffer ( & uniforms , sizeof ( uniforms ) ) ;
Draw : : SamplerState * sampler = useNearest | | shaderInfo - > isUpscalingFilter ? samplerNearest_ : samplerLinear_ ;
draw_ - > BindSamplerStates ( 0 , 1 , & sampler ) ;
draw_ - > BindSamplerStates ( 1 , 1 , & sampler ) ;
if ( shaderInfo - > usePreviousFrame )
draw_ - > BindSamplerStates ( 2 , 1 , & sampler ) ;
2023-05-17 17:22:50 +02:00
draw_ - > BindVertexBuffer ( vdata_ , vertsOffset ) ;
2023-04-08 13:02:18 +02:00
draw_ - > Draw ( 4 , 0 ) ;
2021-06-12 10:54:36 -07:00
postShaderOutput = postShaderFramebuffer ;
lastWidth = nextWidth ;
lastHeight = nextHeight ;
} ;
2020-05-15 23:05:05 -07:00
if ( usePostShader ) {
2023-04-09 23:27:22 +02:00
// When we render to temp framebuffers during post, we switch position, not UV.
// The flipping here is only because D3D has a clip coordinate system that doesn't match their screen coordinate system.
2024-02-01 23:26:36 +01:00
// The flipping here is only because D3D has a clip coordinate system that doesn't match their screen coordinate system.
2020-05-11 23:28:50 -07:00
bool flipped = flags & OutputFlags : : POSITION_FLIPPED ;
2023-04-09 23:27:22 +02:00
float y0 = flipped ? 1.0f : - 1.0f ;
float y1 = flipped ? - 1.0f : 1.0f ;
verts [ 4 ] = { - 1.0f , y0 , 0.0f , 0.0f , 0.0f , 0xFFFFFFFF } ; // TL
verts [ 5 ] = { 1.0f , y0 , 0.0f , 1.0f , 0.0f , 0xFFFFFFFF } ; // TR
verts [ 6 ] = { - 1.0f , y1 , 0.0f , 0.0f , 1.0f , 0xFFFFFFFF } ; // BL
verts [ 7 ] = { 1.0f , y1 , 0.0f , 1.0f , 1.0f , 0xFFFFFFFF } ; // BR
2023-04-08 12:53:41 +02:00
// Now, adjust for the desired input rectangle.
verts [ 8 ] = { - 1.0f , y0 , 0.0f , u0 , v0 , 0xFFFFFFFF } ; // TL
verts [ 9 ] = { 1.0f , y0 , 0.0f , u1 , v0 , 0xFFFFFFFF } ; // TR
verts [ 10 ] = { - 1.0f , y1 , 0.0f , u0 , v1 , 0xFFFFFFFF } ; // BL
verts [ 11 ] = { 1.0f , y1 , 0.0f , u1 , v1 , 0xFFFFFFFF } ; // BR
2020-05-10 17:40:08 -07:00
draw_ - > UpdateBuffer ( vdata_ , ( const uint8_t * ) verts , 0 , sizeof ( verts ) , Draw : : UPDATE_DISCARD ) ;
2020-05-15 23:05:05 -07:00
for ( size_t i = 0 ; i < postShaderFramebuffers_ . size ( ) ; + + i ) {
Draw : : Pipeline * postShaderPipeline = postShaderPipelines_ [ i ] ;
const ShaderInfo * shaderInfo = & postShaderInfo_ [ i ] ;
Draw : : Framebuffer * postShaderFramebuffer = postShaderFramebuffers_ [ i ] ;
2021-06-12 10:54:36 -07:00
if ( ! isFinalAtOutputResolution & & i = = postShaderFramebuffers_ . size ( ) - 1 & & ! previousFramebuffers_ . empty ( ) ) {
// This is the last pass and we're going direct to the backbuffer after this.
// Redirect output to a separate framebuffer to keep the previous frame.
previousIndex_ + + ;
2022-03-13 12:03:48 -07:00
if ( previousIndex_ > = ( int ) previousFramebuffers_ . size ( ) )
2021-06-12 10:54:36 -07:00
previousIndex_ = 0 ;
postShaderFramebuffer = previousFramebuffers_ [ previousIndex_ ] ;
}
2020-05-10 17:40:08 -07:00
2022-10-23 11:21:35 +02:00
draw_ - > BindFramebufferAsRenderTarget ( postShaderFramebuffer , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " PostShader " ) ;
2023-04-08 12:53:41 +02:00
// Pick vertices 8-11 for the first pass.
int vertOffset = i = = 0 ? ( int ) sizeof ( Vertex ) * 8 : ( int ) sizeof ( Vertex ) * 4 ;
performShaderPass ( shaderInfo , postShaderFramebuffer , postShaderPipeline , vertOffset ) ;
2020-05-15 23:05:05 -07:00
}
2020-05-16 09:05:53 -07:00
if ( isFinalAtOutputResolution & & postShaderInfo_ . back ( ) . isUpscalingFilter )
useNearest = true ;
2020-05-10 17:40:08 -07:00
} else {
2023-04-08 12:53:41 +02:00
// Only need to update the first four verts, the rest are unused.
2020-05-10 17:40:08 -07:00
draw_ - > UpdateBuffer ( vdata_ , ( const uint8_t * ) verts , 0 , postVertsOffset , Draw : : UPDATE_DISCARD ) ;
}
2021-06-12 10:54:36 -07:00
// If we need to save the previous frame, we have to save any final pass in a framebuffer.
if ( isFinalAtOutputResolution & & ! previousFramebuffers_ . empty ( ) ) {
Draw : : Pipeline * postShaderPipeline = postShaderPipelines_ . back ( ) ;
const ShaderInfo * shaderInfo = & postShaderInfo_ . back ( ) ;
// Pick the next to render to.
previousIndex_ + + ;
2022-03-13 12:03:48 -07:00
if ( previousIndex_ > = ( int ) previousFramebuffers_ . size ( ) )
2021-06-12 10:54:36 -07:00
previousIndex_ = 0 ;
Draw : : Framebuffer * postShaderFramebuffer = previousFramebuffers_ [ previousIndex_ ] ;
2022-10-23 11:21:35 +02:00
draw_ - > BindFramebufferAsRenderTarget ( postShaderFramebuffer , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " InterFrameBlit " ) ;
2023-04-08 12:53:41 +02:00
performShaderPass ( shaderInfo , postShaderFramebuffer , postShaderPipeline , postVertsOffset ) ;
2021-06-12 10:54:36 -07:00
}
2022-10-23 11:21:35 +02:00
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " FinalBlit " ) ;
2020-05-10 00:38:19 -07:00
draw_ - > SetScissorRect ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2022-10-21 12:35:49 +02:00
Draw : : Pipeline * pipeline = ( flags & OutputFlags : : RB_SWIZZLE ) ? texColorRBSwizzle_ : texColor_ ;
2020-05-10 17:40:08 -07:00
2022-10-21 12:35:49 +02:00
if ( useStereo ) {
draw_ - > BindPipeline ( stereoPipeline_ ) ;
if ( ! BindSource ( 0 , true ) ) {
// Fall back
draw_ - > BindPipeline ( texColor_ ) ;
2022-10-22 17:34:23 +02:00
useStereo = false ; // Otherwise we end up uploading the wrong uniforms
2022-10-21 12:35:49 +02:00
}
2020-05-10 00:38:19 -07:00
} else {
2022-10-21 12:35:49 +02:00
if ( isFinalAtOutputResolution & & previousFramebuffers_ . empty ( ) ) {
pipeline = postShaderPipelines_ . back ( ) ;
}
draw_ - > BindPipeline ( pipeline ) ;
if ( postShaderOutput ) {
draw_ - > BindFramebufferAsTexture ( postShaderOutput , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
} else {
BindSource ( 0 , false ) ;
}
2020-05-10 00:38:19 -07:00
}
2022-10-21 12:35:49 +02:00
BindSource ( 1 , false ) ;
2020-05-10 00:38:19 -07:00
2021-06-12 10:54:36 -07:00
if ( isFinalAtOutputResolution & & previousFramebuffers_ . empty ( ) ) {
2020-05-31 19:45:28 +02:00
CalculatePostShaderUniforms ( lastWidth , lastHeight , ( int ) rc . w , ( int ) rc . h , & postShaderInfo_ . back ( ) , & uniforms ) ;
2020-05-10 16:53:15 -07:00
draw_ - > UpdateDynamicUniformBuffer ( & uniforms , sizeof ( uniforms ) ) ;
2022-10-22 17:34:23 +02:00
} else if ( useStereo ) {
CalculatePostShaderUniforms ( lastWidth , lastHeight , ( int ) rc . w , ( int ) rc . h , stereoShaderInfo_ , & uniforms ) ;
draw_ - > UpdateDynamicUniformBuffer ( & uniforms , sizeof ( uniforms ) ) ;
2020-05-10 16:53:15 -07:00
} else {
Draw : : VsTexColUB ub { } ;
2023-02-25 13:09:44 +01:00
memcpy ( ub . WorldViewProj , g_display . rot_matrix . m , sizeof ( float ) * 16 ) ;
2020-05-10 16:53:15 -07:00
draw_ - > UpdateDynamicUniformBuffer ( & ub , sizeof ( ub ) ) ;
}
2020-05-10 17:40:08 -07:00
2023-05-17 17:22:50 +02:00
draw_ - > BindVertexBuffer ( vdata_ , 0 ) ;
2020-05-10 00:38:19 -07:00
2020-05-10 16:53:15 -07:00
Draw : : SamplerState * sampler = useNearest ? samplerNearest_ : samplerLinear_ ;
draw_ - > BindSamplerStates ( 0 , 1 , & sampler ) ;
2020-05-17 10:47:39 -07:00
draw_ - > BindSamplerStates ( 1 , 1 , & sampler ) ;
2020-05-10 16:53:15 -07:00
2020-05-10 00:38:19 -07:00
auto setViewport = [ & ] ( float x , float y , float w , float h ) {
Draw : : Viewport viewport { x , y , w , h , 0.0f , 1.0f } ;
2023-02-24 22:09:32 +01:00
draw_ - > SetViewport ( viewport ) ;
2020-05-10 00:38:19 -07:00
} ;
2020-05-15 22:24:55 -07:00
CardboardSettings cardboardSettings ;
GetCardboardSettings ( & cardboardSettings ) ;
2020-05-10 00:38:19 -07:00
if ( cardboardSettings . enabled ) {
2022-10-21 12:35:49 +02:00
// TODO: This could actually support stereo now, with an appropriate shader.
2020-05-10 00:38:19 -07:00
// This is what the left eye sees.
setViewport ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2023-04-08 13:02:18 +02:00
draw_ - > Draw ( 4 , 0 ) ;
2020-05-10 00:38:19 -07:00
// And this is the right eye, unless they're a pirate.
setViewport ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
2023-04-08 13:02:18 +02:00
draw_ - > Draw ( 4 , 0 ) ;
2020-05-10 00:38:19 -07:00
} else {
setViewport ( 0.0f , 0.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ ) ;
2023-04-08 13:02:18 +02:00
draw_ - > Draw ( 4 , 0 ) ;
2020-05-10 00:38:19 -07:00
}
DoRelease ( srcFramebuffer_ ) ;
DoRelease ( srcTexture_ ) ;
2020-05-10 18:19:35 -07:00
2020-07-13 23:11:52 +02:00
// Unbinds all textures and samplers too, needed since sometimes a MakePixelTexture is deleted etc.
2022-12-01 19:15:38 +01:00
draw_ - > Invalidate ( InvalidationFlags : : CACHED_RENDER_STATE ) ;
2021-06-12 11:27:26 -07:00
previousUniforms_ = uniforms ;
2023-12-11 13:06:15 +01:00
presentedThisFrame_ = true ;
2020-05-09 22:06:22 -07:00
}
2020-05-16 00:31:14 -07:00
2022-10-21 15:42:32 +02:00
void PresentationCommon : : CalculateRenderResolution ( int * width , int * height , int * scaleFactor , bool * upscaling , bool * ssaa ) const {
2020-05-16 00:31:14 -07:00
// Check if postprocessing shader is doing upscaling as it requires native resolution
std : : vector < const ShaderInfo * > shaderInfo ;
2021-04-10 18:35:23 -07:00
if ( ! g_Config . vPostShaderNames . empty ( ) ) {
2021-09-28 23:35:57 +02:00
ReloadAllPostShaderInfo ( draw_ ) ;
2022-12-07 22:28:55 +01:00
RemoveUnknownPostShaders ( & g_Config . vPostShaderNames ) ;
2022-12-09 17:19:08 +01:00
FixPostShaderOrder ( & g_Config . vPostShaderNames ) ;
2020-05-18 11:17:45 +02:00
shaderInfo = GetFullPostShadersChain ( g_Config . vPostShaderNames ) ;
2020-05-16 00:31:14 -07:00
}
bool firstIsUpscalingFilter = shaderInfo . empty ( ) ? false : shaderInfo . front ( ) - > isUpscalingFilter ;
int firstSSAAFilterLevel = shaderInfo . empty ( ) ? 0 : shaderInfo . front ( ) - > SSAAFilterLevel ;
2023-04-11 22:27:03 +02:00
// In auto mode (zoom == 0), round up to an integer zoom factor for the render size.
2020-05-16 00:31:14 -07:00
int zoom = g_Config . iInternalResolution ;
if ( zoom = = 0 | | firstSSAAFilterLevel > = 2 ) {
// auto mode, use the longest dimension
if ( ! g_Config . IsPortrait ( ) ) {
zoom = ( PSP_CoreParameter ( ) . pixelWidth + 479 ) / 480 ;
} else {
zoom = ( PSP_CoreParameter ( ) . pixelHeight + 479 ) / 480 ;
}
if ( firstSSAAFilterLevel > = 2 )
zoom * = firstSSAAFilterLevel ;
}
if ( zoom < = 1 | | firstIsUpscalingFilter )
zoom = 1 ;
if ( upscaling ) {
* upscaling = firstIsUpscalingFilter ;
for ( auto & info : shaderInfo ) {
* upscaling = * upscaling | | info - > isUpscalingFilter ;
}
}
if ( ssaa ) {
* ssaa = firstSSAAFilterLevel > = 2 ;
for ( auto & info : shaderInfo ) {
* ssaa = * ssaa | | info - > SSAAFilterLevel > = 2 ;
}
}
2022-12-02 20:48:35 +01:00
if ( IsVREnabled ( ) ) {
* width = 480 * zoom ;
* height = 480 * zoom ;
2020-05-16 00:31:14 -07:00
} else {
2023-04-11 22:27:03 +02:00
// Note: We previously checked g_Config.IsPortrait (internal rotation) here but that was wrong -
// we still render at 480x272 * zoom.
2020-05-16 00:31:14 -07:00
* width = 480 * zoom ;
* height = 272 * zoom ;
}
2020-11-05 11:21:00 +01:00
* scaleFactor = zoom ;
2020-05-16 00:31:14 -07:00
}