1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

VK/GL: honor game's aspect ratio when scaling

This commit is contained in:
Megamouse 2021-11-02 21:57:02 +01:00
parent f16949c292
commit c8d4a0dcdc
7 changed files with 132 additions and 72 deletions

View File

@ -203,7 +203,7 @@ error_code cellVideoOutConfigure(u32 videoOut, vm::ptr<CellVideoOutConfiguration
conf.aspect = g_video_out_aspect_id.at(g_cfg.video.aspect_ratio);
}
cellSysutil.notice("Selected video resolution 0x%x", config->resolutionId);
cellSysutil.notice("Selected video configuration: resolutionId=0x%x, aspect=0x%x, format=0x%x", config->resolutionId, config->aspect, config->format);
return CELL_OK;
}

View File

@ -34,7 +34,7 @@ enum CellVideoOutResolutionId : s32
CELL_VIDEO_OUT_RESOLUTION_1280x1080 = 0xc,
CELL_VIDEO_OUT_RESOLUTION_960x1080 = 0xd,
CELL_VIDEO_OUT_RESOLUTION_720_3D_FRAME_PACKING = 0x81,
CELL_VIDEO_OUT_RESOLUTION_1024x720_3D_FRAME_PACKING = 0x88,
CELL_VIDEO_OUT_RESOLUTION_1024x720_3D_FRAME_PACKING = 0x88,
CELL_VIDEO_OUT_RESOLUTION_960x720_3D_FRAME_PACKING = 0x89,
CELL_VIDEO_OUT_RESOLUTION_800x720_3D_FRAME_PACKING = 0x8a,
CELL_VIDEO_OUT_RESOLUTION_640x720_3D_FRAME_PACKING = 0x8b,

View File

@ -137,7 +137,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
if (!buffer_pitch)
buffer_pitch = buffer_width * avconfig.get_bpp();
const u32 video_frame_height = (!avconfig._3d? avconfig.resolution_y : (avconfig.resolution_y - 30) / 2);
const u32 video_frame_height = (!avconfig._3d ? avconfig.resolution_y : (avconfig.resolution_y - 30) / 2);
buffer_width = std::min(buffer_width, avconfig.resolution_x);
buffer_height = std::min(buffer_height, video_frame_height);
}
@ -200,25 +200,12 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
// Calculate blit coordinates
coordi aspect_ratio;
sizei csize(width, height);
const sizei csize(width, height);
sizei new_size = csize;
if (!g_cfg.video.stretch_to_display_area)
{
const double aq = 1. * buffer_width / buffer_height;
const double rq = 1. * new_size.width / new_size.height;
const double q = aq / rq;
if (q > 1.0)
{
new_size.height = static_cast<int>(new_size.height / q);
aspect_ratio.y = (csize.height - new_size.height) / 2;
}
else if (q < 1.0)
{
new_size.width = static_cast<int>(new_size.width * q);
aspect_ratio.x = (csize.width - new_size.width) / 2;
}
avconfig.downscale_to_aspect_ratio(aspect_ratio.x, aspect_ratio.y, new_size.width, new_size.height);
}
aspect_ratio.size = new_size;

View File

@ -551,25 +551,12 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
// Calculate output dimensions. Done after swapchain acquisition in case it was recreated.
coordi aspect_ratio;
sizei csize = static_cast<sizei>(m_swapchain_dims);
const sizei csize = static_cast<sizei>(m_swapchain_dims);
sizei new_size = csize;
if (!g_cfg.video.stretch_to_display_area)
{
const double aq = 1. * buffer_width / buffer_height;
const double rq = 1. * new_size.width / new_size.height;
const double q = aq / rq;
if (q > 1.0)
{
new_size.height = static_cast<int>(new_size.height / q);
aspect_ratio.y = (csize.height - new_size.height) / 2;
}
else if (q < 1.0)
{
new_size.width = static_cast<int>(new_size.width * q);
aspect_ratio.x = (csize.width - new_size.width) / 2;
}
avconfig.downscale_to_aspect_ratio(aspect_ratio.x, aspect_ratio.y, new_size.width, new_size.height);
}
aspect_ratio.size = new_size;

View File

@ -2,6 +2,7 @@
#include "rsx_utils.h"
#include "rsx_methods.h"
#include "Emu/RSX/GCM.h"
#include "Emu/Cell/Modules/cellVideoOut.h"
#include "Overlays/overlays.h"
#ifdef _MSC_VER
@ -102,6 +103,94 @@ namespace rsx
}
}
u32 avconf::get_compatible_gcm_format() const
{
switch (format)
{
default:
rsx_log.error("Invalid AV format 0x%x", format);
[[fallthrough]];
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8:
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8:
return CELL_GCM_TEXTURE_A8R8G8B8;
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_R16G16B16X16_FLOAT:
return CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT;
}
}
u8 avconf::get_bpp() const
{
switch (format)
{
default:
rsx_log.error("Invalid AV format 0x%x", format);
[[fallthrough]];
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8:
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8:
return 4;
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_R16G16B16X16_FLOAT:
return 8;
}
}
double avconf::get_aspect_ratio() const
{
video_aspect v_aspect;
switch (aspect)
{
case CELL_VIDEO_OUT_ASPECT_16_9: v_aspect = video_aspect::_16_9; break;
case CELL_VIDEO_OUT_ASPECT_4_3: v_aspect = video_aspect::_4_3; break;
case CELL_VIDEO_OUT_ASPECT_AUTO:
default: v_aspect = g_cfg.video.aspect_ratio; break;
}
double aspect_ratio;
switch (v_aspect)
{
case video_aspect::_4_3: aspect_ratio = 4.0 / 3.0; break;
case video_aspect::_16_9: aspect_ratio = 16.0 / 9.0; break;
}
return aspect_ratio;
}
void avconf::upscale_to_aspect_ratio(int& width, int& height) const
{
if (width == 0 || height == 0) return;
const double old_aspect = 1. * width / height;
const double scaling_factor = get_aspect_ratio() / old_aspect;
if (scaling_factor > 1.0)
{
width = static_cast<int>(width * scaling_factor);
}
else if (scaling_factor < 1.0)
{
height = static_cast<int>(height / scaling_factor);
}
}
void avconf::downscale_to_aspect_ratio(int& x, int& y, int& width, int& height) const
{
if (width == 0 || height == 0) return;
const double old_aspect = 1. * width / height;
const double scaling_factor = get_aspect_ratio() / old_aspect;
if (scaling_factor > 1.0)
{
const int new_height = static_cast<int>(height / scaling_factor);
y = (height - new_height) / 2;
height = new_height;
}
else if (scaling_factor < 1.0)
{
const int new_width = static_cast<int>(width * scaling_factor);
x = (width - new_width) / 2;
width = new_width;
}
}
#ifdef TEXTURE_CACHE_DEBUG
tex_cache_checker_t tex_cache_checker = {};
#endif

View File

@ -161,35 +161,12 @@ namespace rsx
u32 resolution_y = 720; // Y RES
atomic_t<u32> state = 0; // 1 after cellVideoOutConfigure was called
u32 get_compatible_gcm_format() const
{
switch (format)
{
default:
rsx_log.error("Invalid AV format 0x%x", format);
[[fallthrough]];
case 0: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8:
case 1: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8:
return CELL_GCM_TEXTURE_A8R8G8B8;
case 2: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_R16G16B16X16_FLOAT:
return CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT;
}
}
u32 get_compatible_gcm_format() const;
u8 get_bpp() const;
double get_aspect_ratio() const;
u8 get_bpp() const
{
switch (format)
{
default:
rsx_log.error("Invalid AV format 0x%x", format);
[[fallthrough]];
case 0: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8:
case 1: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8:
return 4;
case 2: // CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_R16G16B16X16_FLOAT:
return 8;
}
}
void upscale_to_aspect_ratio(int& width, int& height) const;
void downscale_to_aspect_ratio(int& x, int& y, int& width, int& height) const;
};
struct blit_src_info

View File

@ -10,6 +10,7 @@
#include "Emu/system_progress.hpp"
#include "Emu/IdManager.h"
#include "Emu/Cell/Modules/cellScreenshot.h"
#include "Emu/Cell/Modules/cellVideoOut.h"
#include "Emu/RSX/rsx_utils.h"
#include <QApplication>
@ -618,16 +619,32 @@ void gs_frame::take_screenshot(std::vector<u8> data, const u32 sshot_width, cons
text[num_text].key = const_cast<char*>("Comment");
text[num_text].text = const_cast<char*>(game_comment.c_str());
std::vector<u8*> rows(sshot_height);
for (usz y = 0; y < sshot_height; y++)
rows[y] = sshot_data_alpha.data() + y * sshot_width * 4;
// Create image from data
QImage img(sshot_data_alpha.data(), sshot_width, sshot_height, sshot_width * 4, QImage::Format_RGBA8888);
// Scale image if necessary
int new_width = img.width();
int new_height = img.height();
auto& avconf = g_fxo->get<rsx::avconf>();
avconf.upscale_to_aspect_ratio(new_width, new_height);
if (new_width != img.width() || new_height != img.height())
{
img = img.scaled(QSize(new_width, new_height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation);
img.convertTo(QImage::Format_RGBA8888); // The current Qt version changes the image format during smooth scaling, so we have to change it back.
}
// Create row pointers for libpng
std::vector<u8*> rows(img.height());
for (int y = 0; y < img.height(); y++)
rows[y] = img.scanLine(y);
std::vector<u8> encoded_png;
const auto write_png = [&]()
{
const scoped_png_ptrs ptrs;
png_set_IHDR(ptrs.write_ptr, ptrs.info_ptr, sshot_width, sshot_height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_IHDR(ptrs.write_ptr, ptrs.info_ptr, img.width(), img.height(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_text(ptrs.write_ptr, ptrs.info_ptr, text, 6);
png_set_rows(ptrs.write_ptr, ptrs.info_ptr, &rows[0]);
png_set_write_fn(ptrs.write_ptr, &encoded_png, [](png_structp png_ptr, png_bytep data, png_size_t length)
@ -660,10 +677,13 @@ void gs_frame::take_screenshot(std::vector<u8> data, const u32 sshot_width, cons
// Games choose the overlay file and the offset based on the current video resolution.
// We need to scale the overlay if our resolution scaling causes the image to have a different size.
auto& avconf = g_fxo->get<rsx::avconf>();
// TODO: handle wacky PS3 resolutions (without resolution scaling)
if (avconf.resolution_x != sshot_width || avconf.resolution_y != sshot_height)
// Scale the resolution first (as seen before with the image)
new_width = avconf.resolution_x;
new_height = avconf.resolution_y;
avconf.upscale_to_aspect_ratio(new_width, new_height);
if (new_width != img.width() || new_height != img.height())
{
const int scale = rsx::get_resolution_scale_percent();
const int x = (scale * manager.overlay_offset_x) / 100;
@ -679,16 +699,16 @@ void gs_frame::take_screenshot(std::vector<u8> data, const u32 sshot_width, cons
overlay_img = overlay_img.scaled(QSize(width, height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation);
}
if (manager.overlay_offset_x < static_cast<s64>(sshot_width) &&
manager.overlay_offset_y < static_cast<s64>(sshot_height) &&
if (manager.overlay_offset_x < static_cast<s64>(img.width()) &&
manager.overlay_offset_y < static_cast<s64>(img.height()) &&
manager.overlay_offset_x + overlay_img.width() > 0 &&
manager.overlay_offset_y + overlay_img.height() > 0)
{
QImage screenshot_img(rows[0], sshot_width, sshot_height, QImage::Format_RGBA8888);
QImage screenshot_img(rows[0], img.width(), img.height(), QImage::Format_RGBA8888);
QPainter painter(&screenshot_img);
painter.drawImage(manager.overlay_offset_x, manager.overlay_offset_y, overlay_img);
std::memcpy(rows[0], screenshot_img.constBits(), static_cast<usz>(sshot_height) * screenshot_img.bytesPerLine());
std::memcpy(rows[0], screenshot_img.constBits(), screenshot_img.sizeInBytes());
screenshot_log.success("Applied screenshot overlay '%s'", cell_sshot_overlay_path);
}