Video Backends: Implement vertical scaling for xfb copies. This fixes the

display of PAL games that run in 50hz mode.
This commit is contained in:
iwubcode
2017-07-02 21:24:20 -05:00
parent 5a372020ea
commit b285188de1
8 changed files with 65 additions and 34 deletions

View File

@ -26,6 +26,8 @@ struct EFBEncodeParams
s32 SrcTop;
u32 DestWidth;
u32 ScaleFactor;
float y_scale;
u32 padding[3];
};
PSTextureEncoder::PSTextureEncoder()
@ -45,7 +47,7 @@ void PSTextureEncoder::Init()
// EFB2RAM copies use max (EFB_WIDTH * 4) by (EFB_HEIGHT / 4)
// XFB2RAM copies use max (EFB_WIDTH / 2) by (EFB_HEIGHT)
D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, EFB_WIDTH * 4,
EFB_HEIGHT, 1, 1, D3D11_BIND_RENDER_TARGET);
1024, 1, 1, D3D11_BIND_RENDER_TARGET);
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
CHECK(SUCCEEDED(hr), "create efb encode output texture");
D3D::SetDebugObjectName(m_out, "efb encoder output texture");
@ -127,6 +129,7 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
encode_params.SrcTop = src_rect.top;
encode_params.DestWidth = native_width;
encode_params.ScaleFactor = scale_by_half ? 2 : 1;
encode_params.y_scale = params.y_scale;
D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encodeParams);
@ -134,7 +137,7 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
// Also, box filtering won't be correct for anything other than 1x IR
if (scale_by_half || g_renderer->GetEFBScale() != 1)
if (scale_by_half || g_renderer->GetEFBScale() != 1 || params.y_scale > 1.0f)
D3D::SetLinearCopySampler();
else
D3D::SetPointCopySampler();

View File

@ -50,6 +50,7 @@ struct EncodingProgram
{
SHADER program;
GLint copy_position_uniform;
GLint y_scale_uniform;
};
static std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
@ -166,6 +167,7 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
PanicAlert("Failed to compile texture encoding shader.");
program.copy_position_uniform = glGetUniformLocation(program.program.glprogid, "position");
program.y_scale_uniform = glGetUniformLocation(program.program.glprogid, "y_scale");
return s_encoding_programs.emplace(params, program).first->second;
}
@ -217,7 +219,7 @@ void Shutdown()
// dst_line_size, writeStride in bytes
static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size,
u32 dstHeight, u32 writeStride, bool linearFilter)
u32 dstHeight, u32 writeStride, bool linearFilter, float y_scale)
{
// switch to texture converter frame buffer
// attach render buffer as color destination
@ -233,7 +235,7 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
// Also, box filtering won't be correct for anything other than 1x IR
if (linearFilter || g_renderer->GetEFBScale() != 1)
if (linearFilter || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f)
g_sampler_cache->BindLinearSampler(9);
else
g_sampler_cache->BindNearestSampler(9);
@ -282,13 +284,14 @@ void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 nativ
texconv_shader.program.Bind();
glUniform4i(texconv_shader.copy_position_uniform, src_rect.left, src_rect.top, native_width,
scale_by_half ? 2 : 1);
glUniform1f(texconv_shader.y_scale_uniform, params.y_scale);
const GLuint read_texture = params.depth ?
FramebufferManager::ResolveAndGetDepthTarget(src_rect) :
FramebufferManager::ResolveAndGetRenderTarget(src_rect);
EncodeToRamUsingShader(read_texture, dest_ptr, bytes_per_row, num_blocks_y, memory_stride,
scale_by_half && !params.depth);
scale_by_half && !params.depth, params.y_scale);
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
@ -308,7 +311,7 @@ void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* des
// We enable linear filtering, because the GameCube does filtering in the vertical direction when
// yscale is enabled.
// Otherwise we get jaggies when a game uses yscaling (most PAL games)
EncodeToRamUsingShader(srcTexture, destAddr, dstWidth * 2, dstHeight, dstStride, true);
EncodeToRamUsingShader(srcTexture, destAddr, dstWidth * 2, dstHeight, dstStride, true, 1.0f);
FramebufferManager::SetFramebuffer(0);
OGLTexture::DisableStage(0);
g_renderer->RestoreAPIState();

View File

@ -5,6 +5,7 @@
#include "VideoBackends/Vulkan/TextureConverter.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include <string>
@ -32,6 +33,14 @@
namespace Vulkan
{
namespace
{
struct EFBEncodeParams
{
std::array<s32, 4> position_uniform;
float y_scale;
};
}
TextureConverter::TextureConverter()
{
}
@ -243,14 +252,19 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
VK_NULL_HANDLE, shader);
// Uniform - int4 of left,top,native_width,scale
s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast<s32>(native_width),
scale_by_half ? 2 : 1};
draw.SetPushConstants(position_uniform, sizeof(position_uniform));
EFBEncodeParams encoder_params;
encoder_params.position_uniform[0] = src_rect.left;
encoder_params.position_uniform[1] = src_rect.top;
encoder_params.position_uniform[2] = static_cast<s32>(native_width);
encoder_params.position_uniform[3] = scale_by_half ? 2 : 1;
encoder_params.y_scale = params.y_scale;
draw.SetPushConstants(&encoder_params, sizeof(encoder_params));
// We also linear filtering for both box filtering and downsampling higher resolutions to 1x
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
bool linear_filter = (scale_by_half && !params.depth) || g_renderer->GetEFBScale() != 1;
bool linear_filter = (scale_by_half && !params.depth) || g_renderer->GetEFBScale() != 1 ||
params.y_scale > 1.0f;
draw.SetPSSampler(0, src_texture, linear_filter ? g_object_cache->GetLinearSampler() :
g_object_cache->GetPointSampler());