2020-05-10 05:06:22 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// 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 23:53:15 +00:00
# include <set>
2020-05-15 16:07:07 +00:00
# include <cstdint>
2020-05-31 18:13:35 +00:00
2020-05-10 05:06:22 +00:00
# include "base/display.h"
2020-05-31 18:13:35 +00:00
# include "base/NativeApp.h"
2020-05-10 23:53:15 +00:00
# include "file/vfs.h"
# include "file/zip_read.h"
2020-05-10 05:06:22 +00:00
# include "thin3d/thin3d.h"
2020-08-15 18:53:08 +00:00
# include "Common/TimeUtil.h"
2020-05-10 05:06:22 +00:00
# include "Core/Config.h"
# include "Core/ConfigValues.h"
2020-05-10 23:53:15 +00:00
# include "Core/Host.h"
2020-05-10 05:06:22 +00:00
# include "Core/System.h"
# include "Core/HLE/sceDisplay.h"
# include "GPU/Common/PostShader.h"
# include "GPU/Common/PresentationCommon.h"
2020-05-10 23:53:15 +00:00
# include "GPU/Common/ShaderTranslation.h"
2020-05-10 05:06:22 +00:00
struct Vertex {
float x , y , z ;
float u , v ;
uint32_t rgba ;
} ;
2020-07-05 20:46:04 +00:00
FRect GetScreenFrame ( float pixelWidth , float pixelHeight ) {
2020-05-31 18:13:35 +00:00
FRect rc = FRect {
0.0f ,
0.0f ,
pixelWidth ,
pixelHeight ,
} ;
2020-07-05 20:46:04 +00:00
bool applyInset = ! g_Config . bIgnoreScreenInsets ;
if ( applyInset ) {
// Remove the DPI scale to get back to pixels.
float left = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_LEFT ) / g_dpi_scale_x ;
float right = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_RIGHT ) / g_dpi_scale_x ;
float top = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_TOP ) / g_dpi_scale_y ;
float bottom = System_GetPropertyFloat ( SYSPROP_DISPLAY_SAFE_INSET_BOTTOM ) / g_dpi_scale_y ;
// 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 18:13:35 +00:00
return rc ;
}
void CenterDisplayOutputRect ( FRect * rc , float origW , float origH , const FRect & frame , int rotation ) {
2020-05-10 05:06:22 +00:00
float outW ;
float outH ;
bool rotated = rotation = = ROTATION_LOCKED_VERTICAL | | rotation = = ROTATION_LOCKED_VERTICAL180 ;
if ( g_Config . iSmallDisplayZoomType = = ( int ) SmallDisplayZoom : : STRETCH ) {
2020-05-31 18:13:35 +00:00
outW = frame . w ;
outH = frame . h ;
2020-05-10 05:06:22 +00:00
} else {
if ( g_Config . iSmallDisplayZoomType = = ( int ) SmallDisplayZoom : : MANUAL ) {
2020-06-14 17:58:54 +00:00
float offsetX = ( g_Config . fSmallDisplayOffsetX - 0.5f ) * 2.0f * frame . w + frame . x ;
float offsetY = ( g_Config . fSmallDisplayOffsetY - 0.5f ) * 2.0f * frame . h + frame . y ;
2020-05-10 05:06:22 +00:00
// Have to invert Y for GL
if ( GetGPUBackend ( ) = = GPUBackend : : OPENGL ) {
offsetY = offsetY * - 1.0f ;
}
float customZoom = g_Config . fSmallDisplayZoomLevel ;
float smallDisplayW = origW * customZoom ;
float smallDisplayH = origH * customZoom ;
if ( ! rotated ) {
2020-05-31 18:13:35 +00:00
rc - > x = floorf ( ( ( frame . w - smallDisplayW ) / 2.0f ) + offsetX ) ;
rc - > y = floorf ( ( ( frame . h - smallDisplayH ) / 2.0f ) + offsetY ) ;
2020-05-31 17:45:28 +00:00
rc - > w = floorf ( smallDisplayW ) ;
rc - > h = floorf ( smallDisplayH ) ;
2020-05-10 05:06:22 +00:00
return ;
} else {
2020-05-31 18:13:35 +00:00
rc - > x = floorf ( ( ( frame . w - smallDisplayH ) / 2.0f ) + offsetX ) ;
rc - > y = floorf ( ( ( frame . h - smallDisplayW ) / 2.0f ) + offsetY ) ;
2020-05-31 17:45:28 +00:00
rc - > w = floorf ( smallDisplayH ) ;
rc - > h = floorf ( smallDisplayW ) ;
2020-05-10 05:06:22 +00:00
return ;
}
} else if ( g_Config . iSmallDisplayZoomType = = ( int ) SmallDisplayZoom : : AUTO ) {
// Stretch to 1080 for 272*4. But don't distort if not widescreen (i.e. ultrawide of halfwide.)
2020-05-31 18:13:35 +00:00
float pixelCrop = frame . h / 270.0f ;
2020-05-10 05:06:22 +00:00
float resCommonWidescreen = pixelCrop - floor ( pixelCrop ) ;
2020-05-31 18:13:35 +00:00
if ( ! rotated & & resCommonWidescreen = = 0.0f & & frame . w > = pixelCrop * 480.0f ) {
2020-06-14 17:58:54 +00:00
rc - > x = floorf ( ( frame . w - pixelCrop * 480.0f ) * 0.5f + frame . x ) ;
rc - > y = floorf ( - pixelCrop + frame . y ) ;
2020-05-31 17:45:28 +00:00
rc - > w = floorf ( pixelCrop * 480.0f ) ;
rc - > h = floorf ( pixelCrop * 272.0f ) ;
2020-05-10 05:06:22 +00:00
return ;
}
}
float origRatio = ! rotated ? origW / origH : origH / origW ;
2020-05-31 18:13:35 +00:00
float frameRatio = frame . w / frame . h ;
2020-05-10 05:06:22 +00:00
if ( origRatio > frameRatio ) {
// Image is wider than frame. Center vertically.
2020-05-31 18:13:35 +00:00
outW = frame . w ;
outH = frame . w / origRatio ;
2020-05-10 05:06:22 +00:00
// Stretch a little bit
if ( ! rotated & & g_Config . iSmallDisplayZoomType = = ( int ) SmallDisplayZoom : : PARTIAL_STRETCH )
2020-05-31 18:13:35 +00:00
outH = ( frame . h + outH ) / 2.0f ; // (408 + 720) / 2 = 564
2020-05-10 05:06:22 +00:00
} else {
// Image is taller than frame. Center horizontally.
2020-05-31 18:13:35 +00:00
outW = frame . h * origRatio ;
outH = frame . h ;
2020-05-10 05:06:22 +00:00
if ( rotated & & g_Config . iSmallDisplayZoomType = = ( int ) SmallDisplayZoom : : PARTIAL_STRETCH )
2020-05-31 18:13:35 +00:00
outW = ( frame . h + outH ) / 2.0f ; // (408 + 720) / 2 = 564
2020-05-10 05:06:22 +00:00
}
}
2020-05-31 18:13:35 +00:00
rc - > x = floorf ( ( frame . w - outW ) / 2.0f + frame . x ) ;
rc - > y = floorf ( ( frame . h - outH ) / 2.0f + frame . y ) ;
2020-05-31 17:45:28 +00:00
rc - > w = floorf ( outW ) ;
rc - > h = floorf ( outH ) ;
2020-05-10 05:06:22 +00:00
}
PresentationCommon : : PresentationCommon ( Draw : : DrawContext * draw ) : draw_ ( draw ) {
CreateDeviceObjects ( ) ;
}
PresentationCommon : : ~ PresentationCommon ( ) {
DestroyDeviceObjects ( ) ;
}
void PresentationCommon : : GetCardboardSettings ( CardboardSettings * cardboardSettings ) {
2020-05-31 18:13:35 +00:00
if ( ! g_Config . bEnableCardboardVR ) {
cardboardSettings - > enabled = false ;
return ;
}
2020-05-10 05:06:22 +00:00
// Calculate Cardboard Settings
float cardboardScreenScale = g_Config . iCardboardScreenSize / 100.0f ;
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale ;
float cardboardScreenHeight = pixelHeight_ / 2.0f * cardboardScreenScale ;
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 18:13:35 +00:00
cardboardSettings - > enabled = true ;
2020-05-10 05:06:22 +00:00
cardboardSettings - > leftEyeXPosition = cardboardLeftEyeX ;
cardboardSettings - > rightEyeXPosition = cardboardRightEyeX ;
cardboardSettings - > screenYPosition = cardboardScreenY ;
cardboardSettings - > screenWidth = cardboardScreenWidth ;
cardboardSettings - > screenHeight = cardboardScreenHeight ;
}
2020-05-16 06:41:13 +00:00
void PresentationCommon : : CalculatePostShaderUniforms ( int bufferWidth , int bufferHeight , int targetWidth , int targetHeight , const ShaderInfo * shaderInfo , PostShaderUniforms * uniforms ) {
2020-05-10 05:06:22 +00:00
float u_delta = 1.0f / bufferWidth ;
float v_delta = 1.0f / bufferHeight ;
2020-05-16 06:41:13 +00:00
float u_pixel_delta = 1.0f / targetWidth ;
float v_pixel_delta = 1.0f / targetHeight ;
2020-05-10 05:06:22 +00:00
int flipCount = __DisplayGetFlipCount ( ) ;
int vCount = __DisplayGetVCount ( ) ;
2020-08-15 22:13:19 +00:00
float time [ 4 ] = { ( float ) time_now_d ( ) , ( vCount % 60 ) * 1.0f / 60.0f , ( float ) vCount , ( float ) ( flipCount % 60 ) } ;
2020-05-10 05:06:22 +00: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 ) ) ;
2020-05-16 06:41:13 +00:00
uniforms - > video = hasVideo_ ? 1.0f : 0.0f ;
2020-05-10 23:53:15 +00: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 09:15:38 +00:00
2020-05-16 19:29:22 +00:00
uniforms - > setting [ 0 ] = g_Config . mPostShaderSetting [ shaderInfo - > section + " SettingValue1 " ] ; ;
uniforms - > setting [ 1 ] = g_Config . mPostShaderSetting [ shaderInfo - > section + " SettingValue2 " ] ;
uniforms - > setting [ 2 ] = g_Config . mPostShaderSetting [ shaderInfo - > section + " SettingValue3 " ] ;
uniforms - > setting [ 3 ] = g_Config . mPostShaderSetting [ shaderInfo - > section + " SettingValue4 " ] ;
2020-05-10 23:53:15 +00:00
}
static std : : string ReadShaderSrc ( const std : : string & filename ) {
size_t sz = 0 ;
char * data = ( char * ) VFSReadFile ( filename . c_str ( ) , & sz ) ;
if ( ! data )
return " " ;
std : : string src ( data , sz ) ;
free ( data ) ;
return src ;
}
// Note: called on resize and settings changes.
bool PresentationCommon : : UpdatePostShader ( ) {
2020-05-16 07:03:39 +00:00
std : : vector < const ShaderInfo * > shaderInfo ;
2020-05-10 23:53:15 +00:00
if ( g_Config . sPostShaderName ! = " Off " ) {
ReloadAllPostShaderInfo ( ) ;
2020-05-16 07:03:39 +00:00
shaderInfo = GetPostShaderChain ( g_Config . sPostShaderName ) ;
2020-05-10 23:53:15 +00:00
}
DestroyPostShader ( ) ;
2020-05-16 07:03:39 +00:00
if ( shaderInfo . empty ( ) )
2020-05-10 23:53:15 +00:00
return false ;
2020-05-16 07:03:39 +00:00
for ( int i = 0 ; i < shaderInfo . size ( ) ; + + i ) {
const ShaderInfo * next = i + 1 < shaderInfo . size ( ) ? shaderInfo [ i + 1 ] : nullptr ;
if ( ! BuildPostShader ( shaderInfo [ i ] , next ) ) {
DestroyPostShader ( ) ;
return false ;
}
2020-05-16 05:24:55 +00:00
}
usePostShader_ = true ;
return true ;
}
bool PresentationCommon : : BuildPostShader ( const ShaderInfo * shaderInfo , const ShaderInfo * next ) {
2020-05-10 23:53:15 +00:00
std : : string vsSourceGLSL = ReadShaderSrc ( shaderInfo - > vertexShaderFile ) ;
std : : string fsSourceGLSL = ReadShaderSrc ( shaderInfo - > fragmentShaderFile ) ;
if ( vsSourceGLSL . empty ( ) | | fsSourceGLSL . empty ( ) ) {
return false ;
}
std : : string vsError , fsError ;
Draw : : ShaderModule * vs = CompileShaderModule ( Draw : : ShaderStage : : VERTEX , GLSL_140 , vsSourceGLSL , & vsError ) ;
Draw : : ShaderModule * fs = CompileShaderModule ( Draw : : ShaderStage : : FRAGMENT , GLSL_140 , fsSourceGLSL , & fsError ) ;
// Don't worry, CompileShaderModule makes sure they get freed if one succeeded.
if ( ! fs | | ! vs ) {
std : : string errorString = vsError + " \n " + fsError ;
// DO NOT turn this into a report, as it will pollute our logs with all kinds of
// 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 ;
}
Draw : : UniformBufferDesc postShaderDesc { sizeof ( PostShaderUniforms ) , {
{ " gl_HalfPixel " , 0 , - 1 , Draw : : UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , gl_HalfPixel ) } ,
{ " u_texelDelta " , 1 , 1 , Draw : : UniformType : : FLOAT2 , offsetof ( PostShaderUniforms , texelDelta ) } ,
{ " u_pixelDelta " , 2 , 2 , Draw : : UniformType : : FLOAT2 , offsetof ( PostShaderUniforms , pixelDelta ) } ,
{ " u_time " , 3 , 3 , Draw : : UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , time ) } ,
2020-05-16 07:27:53 +00:00
{ " u_setting " , 4 , 4 , Draw : : UniformType : : FLOAT4 , offsetof ( PostShaderUniforms , setting ) } ,
{ " u_video " , 5 , 5 , Draw : : UniformType : : FLOAT1 , offsetof ( PostShaderUniforms , video ) } ,
2020-05-10 23:53:15 +00:00
} } ;
Draw : : Pipeline * pipeline = CreatePipeline ( { vs , fs } , true , & postShaderDesc ) ;
if ( ! pipeline )
return false ;
2020-05-16 05:24:55 +00:00
if ( ! shaderInfo - > outputResolution | | next ) {
int nextWidth = renderWidth_ ;
int nextHeight = renderHeight_ ;
2020-05-16 17:21:00 +00: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-16 05:24:55 +00: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 17:45:28 +00:00
FRect rc ;
2020-07-05 20:46:04 +00:00
FRect frame = GetScreenFrame ( ( float ) pixelWidth_ , ( float ) pixelHeight_ ) ;
2020-05-31 18:13:35 +00:00
CenterDisplayOutputRect ( & rc , 480.0f , 272.0f , frame , g_Config . iInternalScreenRotation ) ;
2020-05-31 17:45:28 +00:00
nextWidth = ( int ) rc . w ;
nextHeight = ( int ) rc . h ;
2020-05-16 05:24:55 +00:00
}
2020-05-16 17:02:12 +00:00
if ( ! AllocateFramebuffer ( nextWidth , nextHeight ) ) {
2020-05-16 05:24:55 +00:00
pipeline - > Release ( ) ;
return false ;
}
}
2020-05-10 23:53:15 +00:00
postShaderPipelines_ . push_back ( pipeline ) ;
2020-05-16 05:24:55 +00:00
postShaderInfo_ . push_back ( * shaderInfo ) ;
2020-05-10 23:53:15 +00:00
return true ;
}
2020-05-16 17:02:12 +00: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
2020-08-09 07:35:56 +00:00
Draw : : Framebuffer * fbo = draw_ - > CreateFramebuffer ( { w , h , 1 , 1 , false , Draw : : FBO_8888 , " presentation " } ) ;
2020-05-16 17:02:12 +00:00
if ( ! fbo ) {
return false ;
}
postShaderFBOUsage_ . push_back ( { fbo , w , h } ) ;
postShaderFramebuffers_ . push_back ( fbo ) ;
return true ;
}
2020-05-10 23:53:15 +00: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 ( ) ) {
host - > NotifyUserMessage ( " Post-shader error: " + firstLine + " ... " , 10.0f , 0xFF3090FF ) ;
} else {
host - > NotifyUserMessage ( " Post-shader error, see log for details " , 10.0f , 0xFF3090FF ) ;
}
2020-05-10 05:06:22 +00:00
}
void PresentationCommon : : DeviceLost ( ) {
DestroyDeviceObjects ( ) ;
}
void PresentationCommon : : DeviceRestore ( Draw : : DrawContext * draw ) {
draw_ = draw ;
CreateDeviceObjects ( ) ;
}
2020-05-10 23:53:15 +00:00
Draw : : Pipeline * PresentationCommon : : CreatePipeline ( std : : vector < Draw : : ShaderModule * > shaders , bool postShader , const Draw : : UniformBufferDesc * uniformDesc ) {
2020-05-10 05:06:22 +00:00
using namespace Draw ;
2020-05-10 23:53:15 +00:00
Semantic pos = SEM_POSITION ;
Semantic tc = SEM_TEXCOORD0 ;
// Shader translation marks these both as "TEXCOORDs" on HLSL...
if ( postShader & & ( lang_ = = HLSL_D3D11 | | lang_ = = HLSL_D3D11_LEVEL9 | | lang_ = = HLSL_DX9 ) ) {
pos = SEM_TEXCOORD0 ;
tc = SEM_TEXCOORD1 ;
}
2020-05-10 05:06:22 +00:00
// TODO: Maybe get rid of color0.
InputLayoutDesc inputDesc = {
{
{ sizeof ( Vertex ) , false } ,
} ,
{
2020-05-10 23:53:15 +00:00
{ 0 , pos , DataFormat : : R32G32B32_FLOAT , 0 } ,
{ 0 , tc , DataFormat : : R32G32_FLOAT , 12 } ,
2020-05-10 05:06:22 +00:00
{ 0 , SEM_COLOR0 , DataFormat : : R8G8B8A8_UNORM , 20 } ,
} ,
} ;
InputLayout * inputLayout = draw_ - > CreateInputLayout ( inputDesc ) ;
DepthStencilState * depth = draw_ - > CreateDepthStencilState ( { false , false , Comparison : : LESS } ) ;
BlendState * blendstateOff = draw_ - > CreateBlendState ( { false , 0xF } ) ;
RasterState * rasterNoCull = draw_ - > CreateRasterState ( { } ) ;
2020-05-10 23:53:15 +00:00
PipelineDesc pipelineDesc { Primitive : : TRIANGLE_LIST , shaders , inputLayout , depth , blendstateOff , rasterNoCull , uniformDesc } ;
Pipeline * pipeline = draw_ - > CreateGraphicsPipeline ( pipelineDesc ) ;
2020-05-10 05:06:22 +00:00
inputLayout - > Release ( ) ;
depth - > Release ( ) ;
blendstateOff - > Release ( ) ;
rasterNoCull - > Release ( ) ;
2020-05-10 23:53:15 +00:00
return pipeline ;
}
void PresentationCommon : : CreateDeviceObjects ( ) {
using namespace Draw ;
2020-08-15 22:38:55 +00:00
_assert_ ( vdata_ = = nullptr ) ;
2020-05-10 23:53:15 +00:00
2020-05-11 00:40:08 +00:00
vdata_ = draw_ - > CreateBuffer ( sizeof ( Vertex ) * 8 , BufferUsageFlag : : DYNAMIC | BufferUsageFlag : : VERTEXDATA ) ;
2020-05-10 23:53:15 +00:00
2020-05-16 05:39:27 +00:00
// TODO: Use 4 and a strip?
idata_ = draw_ - > CreateBuffer ( sizeof ( uint16_t ) * 6 , BufferUsageFlag : : DYNAMIC | BufferUsageFlag : : INDEXDATA ) ;
2020-05-16 12:56:38 +00:00
uint16_t indexes [ ] = { 0 , 1 , 2 , 0 , 2 , 3 } ;
draw_ - > UpdateBuffer ( idata_ , ( const uint8_t * ) indexes , 0 , sizeof ( indexes ) , Draw : : UPDATE_DISCARD ) ;
2020-05-13 15:42:43 +00: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 23:53:15 +00: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-10 05:06:22 +00:00
}
template < typename T >
static void DoRelease ( T * & obj ) {
if ( obj )
obj - > Release ( ) ;
obj = nullptr ;
}
2020-05-10 23:53:15 +00:00
template < typename T >
static void DoReleaseVector ( std : : vector < T * > & list ) {
for ( auto & obj : list )
obj - > Release ( ) ;
list . clear ( ) ;
}
2020-05-10 05:06:22 +00:00
void PresentationCommon : : DestroyDeviceObjects ( ) {
DoRelease ( texColor_ ) ;
DoRelease ( texColorRBSwizzle_ ) ;
DoRelease ( samplerNearest_ ) ;
DoRelease ( samplerLinear_ ) ;
DoRelease ( vdata_ ) ;
DoRelease ( idata_ ) ;
2020-05-10 07:38:19 +00:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
2020-05-10 23:53:15 +00:00
2020-05-16 05:24:55 +00:00
restorePostShader_ = usePostShader_ ;
2020-05-10 23:53:15 +00:00
DestroyPostShader ( ) ;
}
void PresentationCommon : : DestroyPostShader ( ) {
usePostShader_ = false ;
DoReleaseVector ( postShaderModules_ ) ;
DoReleaseVector ( postShaderPipelines_ ) ;
DoReleaseVector ( postShaderFramebuffers_ ) ;
2020-05-16 05:24:55 +00:00
postShaderInfo_ . clear ( ) ;
2020-05-16 17:02:12 +00:00
postShaderFBOUsage_ . clear ( ) ;
2020-05-10 23:53:15 +00:00
}
Draw : : ShaderModule * PresentationCommon : : CompileShaderModule ( Draw : : ShaderStage stage , ShaderLanguage lang , const std : : string & src , std : : string * errorString ) {
std : : string translated = src ;
bool translationFailed = false ;
if ( lang ! = lang_ ) {
// Gonna have to upconvert the shader.
if ( ! TranslateShader ( & translated , lang_ , nullptr , src , lang , stage , errorString ) ) {
ERROR_LOG ( FRAMEBUF , " Failed to translate post-shader: %s " , errorString - > c_str ( ) ) ;
return nullptr ;
}
}
Draw : : ShaderLanguage mappedLang ;
// These aren't exact, unfortunately, but we just need the type Draw will accept.
switch ( lang_ ) {
case GLSL_140 :
mappedLang = Draw : : ShaderLanguage : : GLSL_ES_200 ;
break ;
case GLSL_300 :
mappedLang = Draw : : ShaderLanguage : : GLSL_410 ;
break ;
case GLSL_VULKAN :
mappedLang = Draw : : ShaderLanguage : : GLSL_VULKAN ;
break ;
case HLSL_DX9 :
mappedLang = Draw : : ShaderLanguage : : HLSL_D3D9 ;
break ;
case HLSL_D3D11 :
case HLSL_D3D11_LEVEL9 :
mappedLang = Draw : : ShaderLanguage : : HLSL_D3D11 ;
break ;
default :
mappedLang = Draw : : ShaderLanguage : : GLSL_ES_200 ;
break ;
}
Draw : : ShaderModule * shader = draw_ - > CreateShaderModule ( stage , mappedLang , ( const uint8_t * ) translated . c_str ( ) , translated . size ( ) , " postshader " ) ;
if ( shader )
postShaderModules_ . push_back ( shader ) ;
return shader ;
2020-05-10 05:06:22 +00:00
}
2020-05-16 06:41:13 +00:00
void PresentationCommon : : SourceTexture ( Draw : : Texture * texture , int bufferWidth , int bufferHeight ) {
2020-05-10 07:38:19 +00:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
texture - > AddRef ( ) ;
srcTexture_ = texture ;
2020-05-16 06:41:13 +00:00
srcWidth_ = bufferWidth ;
srcHeight_ = bufferHeight ;
2020-05-10 07:38:19 +00:00
}
2020-05-16 06:41:13 +00:00
void PresentationCommon : : SourceFramebuffer ( Draw : : Framebuffer * fb , int bufferWidth , int bufferHeight ) {
2020-05-10 07:38:19 +00:00
DoRelease ( srcTexture_ ) ;
DoRelease ( srcFramebuffer_ ) ;
fb - > AddRef ( ) ;
srcFramebuffer_ = fb ;
2020-05-16 06:41:13 +00:00
srcWidth_ = bufferWidth ;
srcHeight_ = bufferHeight ;
2020-05-10 07:38:19 +00:00
}
2020-05-17 17:47:39 +00:00
void PresentationCommon : : BindSource ( int binding ) {
2020-05-10 23:53:15 +00:00
if ( srcTexture_ ) {
2020-05-17 17:47:39 +00:00
draw_ - > BindTexture ( binding , srcTexture_ ) ;
2020-05-10 23:53:15 +00:00
} else if ( srcFramebuffer_ ) {
2020-05-17 17:47:39 +00:00
draw_ - > BindFramebufferAsTexture ( srcFramebuffer_ , binding , Draw : : FB_COLOR_BIT , 0 ) ;
2020-05-10 23:53:15 +00:00
} else {
2020-08-15 22:38:55 +00:00
_assert_ ( false ) ;
2020-05-10 23:53:15 +00:00
}
}
2020-05-16 06:41:13 +00:00
void PresentationCommon : : UpdateUniforms ( bool hasVideo ) {
hasVideo_ = hasVideo ;
}
void PresentationCommon : : CopyToOutput ( OutputFlags flags , int uvRotation , float u0 , float v0 , float u1 , float v1 ) {
2020-05-10 23:53:15 +00:00
// Make sure Direct3D 11 clears state, since we set shaders outside Draw.
draw_ - > BindPipeline ( nullptr ) ;
// 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 ;
2020-05-16 05:24:55 +00:00
const bool usePostShader = usePostShader_ & & ! ( flags & OutputFlags : : RB_SWIZZLE ) ;
const bool isFinalAtOutputResolution = usePostShader & & postShaderFramebuffers_ . size ( ) < postShaderPipelines_ . size ( ) ;
2020-05-11 00:40:08 +00:00
bool usePostShaderOutput = false ;
2020-05-16 06:41:13 +00:00
int lastWidth = srcWidth_ ;
int lastHeight = srcHeight_ ;
2020-05-10 23:53:15 +00:00
2020-05-10 07:38:19 +00:00
// These are the output coordinates.
2020-07-05 20:46:04 +00:00
FRect frame = GetScreenFrame ( ( float ) pixelWidth_ , ( float ) pixelHeight_ ) ;
2020-05-31 17:45:28 +00:00
FRect rc ;
2020-05-31 18:13:35 +00:00
CenterDisplayOutputRect ( & rc , 480.0f , 272.0f , frame , uvRotation ) ;
2020-05-10 07:38:19 +00:00
if ( GetGPUBackend ( ) = = GPUBackend : : DIRECT3D9 ) {
2020-05-31 17:45:28 +00:00
rc . x - = 0.5f ;
2020-05-10 23:53:15 +00:00
// This is plus because the top is larger y.
2020-05-31 17:45:28 +00:00
rc . y + = 0.5f ;
2020-05-10 07:38:19 +00:00
}
2020-05-12 06:28:50 +00:00
if ( ( flags & OutputFlags : : BACKBUFFER_FLIPPED ) | | ( flags & OutputFlags : : POSITION_FLIPPED ) ) {
2020-05-10 07:38:19 +00:00
std : : swap ( v0 , v1 ) ;
}
2020-05-11 00:40:08 +00:00
// To make buffer updates easier, we use one array of verts.
int postVertsOffset = ( int ) sizeof ( Vertex ) * 4 ;
Vertex verts [ 8 ] = {
2020-05-31 17:45:28 +00:00
{ rc . x , rc . y , 0 , u0 , v0 , 0xFFFFFFFF } , // TL
{ rc . x , rc . y + rc . h , 0 , u0 , v1 , 0xFFFFFFFF } , // BL
{ rc . x + rc . w , rc . y + rc . h , 0 , u1 , v1 , 0xFFFFFFFF } , // BR
{ rc . x + rc . w , rc . y , 0 , u1 , v0 , 0xFFFFFFFF } , // TR
2020-05-10 05:06:22 +00:00
} ;
2020-05-10 07:38:19 +00:00
float invDestW = 1.0f / ( pixelWidth_ * 0.5f ) ;
float invDestH = 1.0f / ( pixelHeight_ * 0.5f ) ;
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-12 06:28:50 +00:00
case ROTATION_LOCKED_VERTICAL : rotation = 3 ; break ;
case ROTATION_LOCKED_VERTICAL180 : rotation = 1 ; break ;
2020-05-10 07:38:19 +00:00
}
2020-05-12 06:28:50 +00: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 07:38:19 +00:00
}
for ( int i = 0 ; i < 4 ; i + + ) {
temp [ i ] . u = verts [ ( i + rotation ) & 3 ] . u ;
temp [ i ] . v = verts [ ( i + rotation ) & 3 ] . v ;
}
for ( int i = 0 ; i < 4 ; i + + ) {
verts [ i ] . u = temp [ i ] . u ;
verts [ i ] . v = temp [ i ] . v ;
}
}
2020-05-16 05:24:55 +00:00
if ( isFinalAtOutputResolution ) {
2020-05-11 00:40:08 +00:00
// In this mode, we ignore the g_display_rot_matrix. Apply manually.
if ( g_display_rotation ! = DisplayRotation : : ROTATE_0 ) {
for ( int i = 0 ; i < 4 ; i + + ) {
Lin : : Vec3 v ( verts [ i ] . x , verts [ i ] . y , verts [ i ] . z ) ;
// Backwards notation, should fix that...
v = v * g_display_rot_matrix ;
verts [ i ] . x = v . x ;
verts [ i ] . y = v . y ;
}
}
}
2020-05-10 05:06:22 +00:00
2020-05-24 14:51:37 +00:00
if ( flags & OutputFlags : : PILLARBOX ) {
for ( int i = 0 ; i < 4 ; i + + ) {
// Looks about right.
verts [ i ] . x * = 0.75f ;
}
}
2020-05-16 06:05:05 +00:00
if ( usePostShader ) {
2020-05-12 06:28:50 +00:00
bool flipped = flags & OutputFlags : : POSITION_FLIPPED ;
2020-05-11 03:29:28 +00:00
float post_v0 = ! flipped ? 1.0f : 0.0f ;
float post_v1 = ! flipped ? 0.0f : 1.0f ;
2020-05-11 00:40:08 +00:00
verts [ 4 ] = { - 1 , - 1 , 0 , 0 , post_v1 , 0xFFFFFFFF } ; // TL
verts [ 5 ] = { - 1 , 1 , 0 , 0 , post_v0 , 0xFFFFFFFF } ; // BL
verts [ 6 ] = { 1 , 1 , 0 , 1 , post_v0 , 0xFFFFFFFF } ; // BR
verts [ 7 ] = { 1 , - 1 , 0 , 1 , post_v1 , 0xFFFFFFFF } ; // TR
draw_ - > UpdateBuffer ( vdata_ , ( const uint8_t * ) verts , 0 , sizeof ( verts ) , Draw : : UPDATE_DISCARD ) ;
2020-05-16 06:05:05 +00: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 ] ;
2020-05-11 00:40:08 +00:00
2020-05-21 09:24:05 +00:00
draw_ - > BindFramebufferAsRenderTarget ( postShaderFramebuffer , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " PostShader " ) ;
2020-05-16 06:05:05 +00:00
if ( usePostShaderOutput ) {
draw_ - > BindFramebufferAsTexture ( postShaderFramebuffers_ [ i - 1 ] , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
} else {
2020-05-17 17:47:39 +00:00
BindSource ( 0 ) ;
2020-05-16 06:05:05 +00:00
}
2020-05-17 17:47:39 +00:00
BindSource ( 1 ) ;
2020-05-16 06:05:05 +00:00
2020-05-16 06:41:13 +00:00
int nextWidth , nextHeight ;
draw_ - > GetFramebufferDimensions ( postShaderFramebuffer , & nextWidth , & nextHeight ) ;
Draw : : Viewport viewport { 0 , 0 , ( float ) nextWidth , ( float ) nextHeight , 0.0f , 1.0f } ;
2020-05-16 06:05:05 +00:00
draw_ - > SetViewports ( 1 , & viewport ) ;
2020-05-16 06:41:13 +00:00
draw_ - > SetScissorRect ( 0 , 0 , nextWidth , nextHeight ) ;
PostShaderUniforms uniforms ;
CalculatePostShaderUniforms ( lastWidth , lastHeight , nextWidth , nextHeight , shaderInfo , & uniforms ) ;
2020-05-16 06:05:05 +00:00
draw_ - > BindPipeline ( postShaderPipeline ) ;
draw_ - > UpdateDynamicUniformBuffer ( & uniforms , sizeof ( uniforms ) ) ;
2020-05-16 16:05:53 +00:00
Draw : : SamplerState * sampler = useNearest | | shaderInfo - > isUpscalingFilter ? samplerNearest_ : samplerLinear_ ;
2020-05-16 06:05:05 +00:00
draw_ - > BindSamplerStates ( 0 , 1 , & sampler ) ;
2020-05-17 17:47:39 +00:00
draw_ - > BindSamplerStates ( 1 , 1 , & sampler ) ;
2020-05-16 06:05:05 +00:00
draw_ - > BindVertexBuffers ( 0 , 1 , & vdata_ , & postVertsOffset ) ;
draw_ - > BindIndexBuffer ( idata_ , 0 ) ;
draw_ - > DrawIndexed ( 6 , 0 ) ;
draw_ - > BindIndexBuffer ( nullptr , 0 ) ;
usePostShaderOutput = true ;
2020-05-16 06:41:13 +00:00
lastWidth = nextWidth ;
lastHeight = nextHeight ;
2020-05-16 06:05:05 +00:00
}
2020-05-16 16:05:53 +00:00
if ( isFinalAtOutputResolution & & postShaderInfo_ . back ( ) . isUpscalingFilter )
useNearest = true ;
2020-05-11 00:40:08 +00:00
} else {
draw_ - > UpdateBuffer ( vdata_ , ( const uint8_t * ) verts , 0 , postVertsOffset , Draw : : UPDATE_DISCARD ) ;
}
Draw : : Pipeline * pipeline = flags & OutputFlags : : RB_SWIZZLE ? texColorRBSwizzle_ : texColor_ ;
2020-05-16 05:24:55 +00:00
if ( isFinalAtOutputResolution ) {
2020-05-16 06:05:05 +00:00
pipeline = postShaderPipelines_ . back ( ) ;
2020-05-11 00:40:08 +00:00
}
2020-05-10 05:06:22 +00:00
2020-05-21 09:24:05 +00:00
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " FinalBlit " ) ;
2020-05-10 07:38:19 +00:00
draw_ - > SetScissorRect ( 0 , 0 , pixelWidth_ , pixelHeight_ ) ;
2020-05-11 00:40:08 +00:00
draw_ - > BindPipeline ( pipeline ) ;
if ( usePostShaderOutput ) {
2020-05-10 23:53:15 +00:00
draw_ - > BindFramebufferAsTexture ( postShaderFramebuffers_ . back ( ) , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
2020-05-10 07:38:19 +00:00
} else {
2020-05-17 17:47:39 +00:00
BindSource ( 0 ) ;
2020-05-10 07:38:19 +00:00
}
2020-05-17 17:47:39 +00:00
BindSource ( 1 ) ;
2020-05-10 07:38:19 +00:00
2020-05-16 05:24:55 +00:00
if ( isFinalAtOutputResolution ) {
2020-05-16 06:41:13 +00:00
PostShaderUniforms uniforms ;
2020-05-31 17:45:28 +00:00
CalculatePostShaderUniforms ( lastWidth , lastHeight , ( int ) rc . w , ( int ) rc . h , & postShaderInfo_ . back ( ) , & uniforms ) ;
2020-05-10 23:53:15 +00:00
draw_ - > UpdateDynamicUniformBuffer ( & uniforms , sizeof ( uniforms ) ) ;
} else {
Draw : : VsTexColUB ub { } ;
memcpy ( ub . WorldViewProj , g_display_rot_matrix . m , sizeof ( float ) * 16 ) ;
draw_ - > UpdateDynamicUniformBuffer ( & ub , sizeof ( ub ) ) ;
}
2020-05-11 00:40:08 +00:00
2020-05-10 05:06:22 +00:00
draw_ - > BindVertexBuffers ( 0 , 1 , & vdata_ , nullptr ) ;
draw_ - > BindIndexBuffer ( idata_ , 0 ) ;
2020-05-10 07:38:19 +00:00
2020-05-10 23:53:15 +00:00
Draw : : SamplerState * sampler = useNearest ? samplerNearest_ : samplerLinear_ ;
draw_ - > BindSamplerStates ( 0 , 1 , & sampler ) ;
2020-05-17 17:47:39 +00:00
draw_ - > BindSamplerStates ( 1 , 1 , & sampler ) ;
2020-05-10 23:53:15 +00:00
2020-05-10 07:38:19 +00:00
auto setViewport = [ & ] ( float x , float y , float w , float h ) {
Draw : : Viewport viewport { x , y , w , h , 0.0f , 1.0f } ;
draw_ - > SetViewports ( 1 , & viewport ) ;
} ;
2020-05-16 05:24:55 +00:00
CardboardSettings cardboardSettings ;
GetCardboardSettings ( & cardboardSettings ) ;
2020-05-10 07:38:19 +00:00
if ( cardboardSettings . enabled ) {
// This is what the left eye sees.
setViewport ( cardboardSettings . leftEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
draw_ - > DrawIndexed ( 6 , 0 ) ;
// And this is the right eye, unless they're a pirate.
setViewport ( cardboardSettings . rightEyeXPosition , cardboardSettings . screenYPosition , cardboardSettings . screenWidth , cardboardSettings . screenHeight ) ;
draw_ - > DrawIndexed ( 6 , 0 ) ;
} else {
setViewport ( 0.0f , 0.0f , ( float ) pixelWidth_ , ( float ) pixelHeight_ ) ;
draw_ - > DrawIndexed ( 6 , 0 ) ;
}
2020-05-10 05:06:22 +00:00
draw_ - > BindIndexBuffer ( nullptr , 0 ) ;
2020-05-10 07:38:19 +00:00
DoRelease ( srcFramebuffer_ ) ;
DoRelease ( srcTexture_ ) ;
2020-05-11 01:19:35 +00:00
2020-07-13 21:11:52 +00:00
// Unbinds all textures and samplers too, needed since sometimes a MakePixelTexture is deleted etc.
2020-05-11 01:19:35 +00:00
draw_ - > BindPipeline ( nullptr ) ;
2020-05-10 05:06:22 +00:00
}
2020-05-16 07:31:14 +00:00
void PresentationCommon : : CalculateRenderResolution ( int * width , int * height , bool * upscaling , bool * ssaa ) {
// Check if postprocessing shader is doing upscaling as it requires native resolution
std : : vector < const ShaderInfo * > shaderInfo ;
if ( g_Config . sPostShaderName ! = " Off " ) {
ReloadAllPostShaderInfo ( ) ;
shaderInfo = GetPostShaderChain ( g_Config . sPostShaderName ) ;
}
bool firstIsUpscalingFilter = shaderInfo . empty ( ) ? false : shaderInfo . front ( ) - > isUpscalingFilter ;
int firstSSAAFilterLevel = shaderInfo . empty ( ) ? 0 : shaderInfo . front ( ) - > SSAAFilterLevel ;
// Actually, auto mode should be more granular...
// Round up to a zoom factor for the render size.
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 ;
}
}
if ( g_Config . IsPortrait ( ) ) {
* width = 272 * zoom ;
* height = 480 * zoom ;
} else {
* width = 480 * zoom ;
* height = 272 * zoom ;
}
}