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:
parent
f16949c292
commit
c8d4a0dcdc
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user