Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental

This commit is contained in:
Gliniak 2022-07-03 13:07:15 +02:00
commit a8df744ea6
17 changed files with 124 additions and 119 deletions

View File

@ -693,6 +693,7 @@ EmulatorWindow::GetGuestOutputPaintEffectForCvarValue(
ui::Presenter::GuestOutputPaintConfig
EmulatorWindow::GetGuestOutputPaintConfigForCvars() {
ui::Presenter::GuestOutputPaintConfig paint_config;
paint_config.SetAllowOverscanCutoff(true);
paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue(
cvars::postprocess_scaling_and_sharpening));
paint_config.SetCasAdditionalSharpness(

View File

@ -919,9 +919,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader,
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
reader->AdvanceRead((count - 4) * sizeof(uint32_t));
if (!ignore_swap_) {
IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height);
}
IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height);
++counter_;
return true;

View File

@ -90,7 +90,6 @@ class CommandProcessor {
virtual void ClearCaches();
void SetIgnoreSwap(bool ignore_swap) { ignore_swap_ = ignore_swap; }
// "Desired" is for the external thread managing the post-processing effect.
SwapPostEffect GetDesiredSwapPostEffect() const {
return swap_post_effect_desired_;
@ -265,8 +264,6 @@ class CommandProcessor {
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;
bool ignore_swap_ = false;
std::queue<std::function<void()>> pending_fns_;
// MicroEngine binary from PM4_ME_INIT

View File

@ -1722,10 +1722,6 @@ void D3D12CommandProcessor::OnGammaRampPWLValueWritten() {
void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
uint32_t frontbuffer_width,
uint32_t frontbuffer_height) {
// FIXME(Triang3l): frontbuffer_ptr is currently unreliable, in the trace
// player it's set to 0, but it's not needed anyway since the fetch constant
// contains the address.
SCOPE_profile_cpu_f("gpu");
ui::Presenter* presenter = graphics_system_->presenter();
@ -1749,12 +1745,9 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
}
D3D12_RESOURCE_DESC swap_texture_desc = swap_texture_resource->GetDesc();
uint32_t draw_resolution_scale_max =
std::max(texture_cache_->draw_resolution_scale_x(),
texture_cache_->draw_resolution_scale_y());
presenter->RefreshGuestOutput(
uint32_t(swap_texture_desc.Width), uint32_t(swap_texture_desc.Height),
1280 * draw_resolution_scale_max, 720 * draw_resolution_scale_max,
1280, 720,
[this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource,
&swap_texture_desc](
ui::Presenter::GuestOutputRefreshContext& context) -> bool {

View File

@ -1147,7 +1147,8 @@ bool D3D12TextureCache::ClampDrawResolutionScaleToMaxSupported(
}
bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
uint32_t start_unscaled, uint32_t length_unscaled) {
uint32_t start_unscaled, uint32_t length_unscaled,
uint32_t length_scaled_alignment_log2) {
assert_true(IsDrawResolutionScaled());
if (length_unscaled == 0) {
@ -1162,8 +1163,12 @@ bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
uint32_t draw_resolution_scale_area =
draw_resolution_scale_x() * draw_resolution_scale_y();
uint64_t first_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
uint64_t last_scaled = uint64_t(start_unscaled + (length_unscaled - 1)) *
draw_resolution_scale_area;
uint64_t length_scaled_alignment_bits =
(UINT64_C(1) << length_scaled_alignment_log2) - 1;
uint64_t last_scaled = (uint64_t(start_unscaled + (length_unscaled - 1)) *
draw_resolution_scale_area +
length_scaled_alignment_bits) &
~length_scaled_alignment_bits;
const ui::d3d12::D3D12Provider& provider =
command_processor_.GetD3D12Provider();
@ -1273,7 +1278,8 @@ bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
}
bool D3D12TextureCache::MakeScaledResolveRangeCurrent(
uint32_t start_unscaled, uint32_t length_unscaled) {
uint32_t start_unscaled, uint32_t length_unscaled,
uint32_t length_scaled_alignment_log2) {
assert_true(IsDrawResolutionScaled());
if (!length_unscaled || start_unscaled >= SharedMemory::kBufferSize ||
@ -1286,8 +1292,12 @@ bool D3D12TextureCache::MakeScaledResolveRangeCurrent(
uint32_t draw_resolution_scale_area =
draw_resolution_scale_x() * draw_resolution_scale_y();
uint64_t start_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
uint64_t length_scaled_alignment_bits =
(UINT64_C(1) << length_scaled_alignment_log2) - 1;
uint64_t length_scaled =
uint64_t(length_unscaled) * draw_resolution_scale_area;
(uint64_t(length_unscaled) * draw_resolution_scale_area +
length_scaled_alignment_bits) &
~length_scaled_alignment_bits;
uint64_t last_scaled = start_scaled + (length_scaled - 1);
// Get one or two buffers that can hold the whole range.
@ -1855,7 +1865,8 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
if (texture_resolution_scaled && (is_base || !scaled_mips_source_set_up)) {
uint32_t guest_size_unscaled = is_base ? d3d12_texture.GetGuestBaseSize()
: d3d12_texture.GetGuestMipsSize();
if (!MakeScaledResolveRangeCurrent(guest_address, guest_size_unscaled)) {
if (!MakeScaledResolveRangeCurrent(guest_address, guest_size_unscaled,
load_shader_info.source_bpe_log2)) {
command_processor_.ReleaseScratchGPUBuffer(copy_buffer,
copy_buffer_state);
return false;

View File

@ -130,14 +130,16 @@ class D3D12TextureCache final : public TextureCache {
uint32_t& scale_x, uint32_t& scale_y,
const ui::d3d12::D3D12Provider& provider);
// Ensures the tiles backing the range in the buffers are allocated.
bool EnsureScaledResolveMemoryCommitted(uint32_t start_unscaled,
uint32_t length_unscaled) override;
bool EnsureScaledResolveMemoryCommitted(
uint32_t start_unscaled, uint32_t length_unscaled,
uint32_t length_scaled_alignment_log2 = 0) override;
// Makes the specified range of up to 1-2 GB currently accessible on the GPU.
// One draw call can access only at most one range - the same memory is
// accessible through different buffers based on the range needed, so aliasing
// barriers are required.
bool MakeScaledResolveRangeCurrent(uint32_t start_unscaled,
uint32_t length_unscaled);
uint32_t length_unscaled,
uint32_t length_scaled_alignment_log2 = 0);
// These functions create a view of the range specified in the last successful
// MakeScaledResolveRangeCurrent call because that function must be called
// before this.

View File

@ -3012,31 +3012,43 @@ void DxbcShaderTranslator::CompletePixelShader() {
// checked, but let's assume this means "always", not "less, equal or
// greater".
// TODO(Triang3l): Check how alpha test works with NaN on Direct3D 9.
a_.OpINE(alpha_test_op_dest, alpha_test_mask_src, dxbc::Src::LU(0b111));
a_.OpINE(alpha_test_op_dest, alpha_test_mask_src,
dxbc::Src::LU(uint32_t(xenos::CompareFunction::kAlways)));
// Don't do the test if the mode is "always".
a_.OpIf(true, alpha_test_op_src);
{
// Do the test. Can't use subtraction and sign because of float specials.
// Do the test.
dxbc::Src alpha_src(
dxbc::Src::R(system_temps_color_[0], dxbc::Src::kWWWW));
dxbc::Src alpha_test_reference_src(LoadSystemConstant(
SystemConstants::Index::kAlphaTestReference,
offsetof(SystemConstants, alpha_test_reference), dxbc::Src::kXXXX));
// Less than.
a_.OpLT(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 0)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
// Equals to.
a_.OpEq(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 1)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
// Greater than.
a_.OpLT(alpha_test_op_dest, alpha_test_reference_src, alpha_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 2)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
// Handle "not equal" specially (specifically as "not equal" so it's true
// for NaN, not "less or greater" which is false for NaN).
a_.OpIEq(alpha_test_op_dest, alpha_test_mask_src,
dxbc::Src::LU(uint32_t(xenos::CompareFunction::kNotEqual)));
a_.OpIf(true, alpha_test_op_src);
{ a_.OpNE(alpha_test_mask_dest, alpha_src, alpha_test_reference_src); }
a_.OpElse();
{
// Less than.
a_.OpLT(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 0)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
// Equals to.
a_.OpEq(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 1)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
// Greater than.
a_.OpLT(alpha_test_op_dest, alpha_test_reference_src, alpha_src);
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
dxbc::Src::LU(~uint32_t(1 << 2)));
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
}
// Close the "not equal" check.
a_.OpEndIf();
// Discard the pixel if it has failed the test.
if (edram_rov_used_) {
a_.OpRetC(false, alpha_test_mask_src);

View File

@ -656,6 +656,13 @@ bool TextureCache::LoadTextureData(Texture& texture) {
TextureKey texture_key = texture.key();
// Implementation may load multiple blocks at once via accesses of up to 128
// bits (R32G32B32A32_UINT), so aligning the size to this value to make sure
// if the texture is small (especially if it's linear), the last blocks won't
// be cut off (hosts may return 0, 0, 0, 0 for the whole R32G32B32A32_UINT
// access for the non-16-aligned tail even if 1...15 bytes are actually
// provided for it).
// Request uploading of the texture data to the shared memory.
// This is also necessary when resolution scaling is used - the texture cache
// relies on shared memory for invalidation of both unscaled and scaled
@ -666,7 +673,8 @@ bool TextureCache::LoadTextureData(Texture& texture) {
bool base_resolved = texture.GetBaseResolved();
if (base_outdated) {
if (!shared_memory().RequestRange(
texture_key.base_page << 12, texture.GetGuestBaseSize(),
texture_key.base_page << 12,
xe::align(texture.GetGuestBaseSize(), UINT32_C(16)),
texture_key.scaled_resolve ? nullptr : &base_resolved)) {
return false;
}
@ -674,7 +682,8 @@ bool TextureCache::LoadTextureData(Texture& texture) {
bool mips_resolved = texture.GetMipsResolved();
if (mips_outdated) {
if (!shared_memory().RequestRange(
texture_key.mip_page << 12, texture.GetGuestMipsSize(),
texture_key.mip_page << 12,
xe::align(texture.GetGuestMipsSize(), UINT32_C(16)),
texture_key.scaled_resolve ? nullptr : &mips_resolved)) {
return false;
}
@ -685,11 +694,11 @@ bool TextureCache::LoadTextureData(Texture& texture) {
// by an actual resolve, but is still included in the texture size, so the
// GPU won't be trying to access unmapped memory.
if (!EnsureScaledResolveMemoryCommitted(texture_key.base_page << 12,
texture.GetGuestBaseSize())) {
texture.GetGuestBaseSize(), 4)) {
return false;
}
if (!EnsureScaledResolveMemoryCommitted(texture_key.mip_page << 12,
texture.GetGuestMipsSize())) {
texture.GetGuestMipsSize(), 4)) {
return false;
}
}

View File

@ -82,8 +82,9 @@ class TextureCache {
void MarkRangeAsResolved(uint32_t start_unscaled, uint32_t length_unscaled);
// Ensures the memory backing the range in the scaled resolve address space is
// allocated and returns whether it is.
virtual bool EnsureScaledResolveMemoryCommitted(uint32_t start_unscaled,
uint32_t length_unscaled) {
virtual bool EnsureScaledResolveMemoryCommitted(
uint32_t start_unscaled, uint32_t length_unscaled,
uint32_t length_scaled_alignment_log2 = 0) {
return false;
}

View File

@ -89,19 +89,15 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode,
bool clear_caches) {
playing_trace_ = true;
// Pass a copy of present_last_copy_ to the thread so it's not accessible by
// multiple threads at once.
bool present_last_copy = present_last_copy_;
graphics_system_->command_processor()->CallInThread([=]() {
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches,
present_last_copy);
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
});
}
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
size_t trace_size,
TracePlaybackMode playback_mode,
bool clear_caches, bool present_last_copy) {
bool clear_caches) {
auto memory = graphics_system_->memory();
auto command_processor = graphics_system_->command_processor();
@ -109,10 +105,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
command_processor->ClearCaches();
}
if (present_last_copy) {
command_processor->SetIgnoreSwap(true);
}
playback_percent_ = 0;
auto trace_end = trace_data + trace_size;
@ -252,11 +244,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
playing_trace_ = false;
if (present_last_copy) {
command_processor->SetIgnoreSwap(false);
command_processor->IssueSwap(0, 1280, 720);
}
playback_event_->Set();
}

View File

@ -32,9 +32,6 @@ class TracePlayer : public TraceReader {
TracePlayer(GraphicsSystem* graphics_system);
GraphicsSystem* graphics_system() const { return graphics_system_; }
void SetPresentLastCopy(bool present_last_copy) {
present_last_copy_ = present_last_copy;
}
int current_frame_index() const { return current_frame_index_; }
int current_command_index() const { return current_command_index_; }
bool is_playing_trace() const { return playing_trace_; }
@ -53,13 +50,9 @@ class TracePlayer : public TraceReader {
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode, bool clear_caches);
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode, bool clear_caches,
bool present_last_copy);
TracePlaybackMode playback_mode, bool clear_caches);
GraphicsSystem* graphics_system_;
// Whether to present the results of the latest resolve instead of displaying
// the front buffer from the trace.
bool present_last_copy_ = false;
int current_frame_index_;
int current_command_index_;
bool playing_trace_ = false;

View File

@ -114,7 +114,7 @@ bool TraceViewer::Setup() {
// Main display window.
assert_true(app_context().IsInUIThread());
window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer",
1920, 1200);
1920, 1080);
window_->AddListener(&window_listener_);
window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput);
if (!window_->Open()) {
@ -135,28 +135,27 @@ bool TraceViewer::Setup() {
graphics_system_ = emulator_->graphics_system();
player_ = std::make_unique<TracePlayer>(graphics_system_);
player_->SetPresentLastCopy(true);
// Setup drawing to the window.
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
presenter_ = graphics_provider.CreatePresenter();
if (!presenter_) {
ui::Presenter* presenter = graphics_system_->presenter();
if (!presenter) {
XELOGE("Failed to initialize the presenter");
return false;
}
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
immediate_drawer_ = graphics_provider.CreateImmediateDrawer();
if (!immediate_drawer_) {
XELOGE("Failed to initialize the immediate drawer");
return false;
}
immediate_drawer_->SetPresenter(presenter_.get());
immediate_drawer_->SetPresenter(presenter);
imgui_drawer_ =
std::make_unique<xe::ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(),
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter,
immediate_drawer_.get());
trace_viewer_dialog_ = std::unique_ptr<TraceViewerDialog>(
new TraceViewerDialog(imgui_drawer_.get(), *this));
window_->SetPresenter(presenter_.get());
window_->SetPresenter(presenter);
return true;
}

View File

@ -21,7 +21,6 @@
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/immediate_drawer.h"
#include "xenia/ui/presenter.h"
#include "xenia/ui/window.h"
#include "xenia/ui/window_listener.h"
#include "xenia/ui/windowed_app.h"
@ -129,7 +128,6 @@ class TraceViewer : public xe::ui::WindowedApp {
GraphicsSystem* graphics_system_ = nullptr;
std::unique_ptr<TracePlayer> player_;
std::unique_ptr<xe::ui::Presenter> presenter_;
std::unique_ptr<xe::ui::ImmediateDrawer> immediate_drawer_;
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
std::unique_ptr<TraceViewerDialog> trace_viewer_dialog_;

View File

@ -269,11 +269,6 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
return;
}
if (!frontbuffer_ptr) {
// Trace viewer does this.
frontbuffer_ptr = last_copy_base_;
}
std::vector<VkCommandBuffer> submit_buffers;
if (frame_open_) {
// TODO(DrChat): If the setup buffer is empty, don't bother queueing it up.
@ -1108,9 +1103,6 @@ bool VulkanCommandProcessor::IssueCopy() {
texture->in_flight_fence = current_batch_fence_;
// For debugging purposes only (trace viewer)
last_copy_base_ = texture->texture_info.memory.base_address;
if (!frame_open_) {
BeginFrame();
} else if (current_render_state_) {

View File

@ -106,9 +106,6 @@ class VulkanCommandProcessor : public CommandProcessor {
uint32_t coher_base_vc_ = 0;
uint32_t coher_size_vc_ = 0;
// Last copy base address, for debugging only.
uint32_t last_copy_base_ = 0;
bool capturing_ = false;
bool trace_requested_ = false;
bool cache_clear_requested_ = false;

View File

@ -354,14 +354,14 @@ void Presenter::PaintFromUIThread(bool force_paint) {
bool Presenter::RefreshGuestOutput(
uint32_t frontbuffer_width, uint32_t frontbuffer_height,
uint32_t screen_width, uint32_t screen_height,
uint32_t display_aspect_ratio_x, uint32_t display_aspect_ratio_y,
std::function<bool(GuestOutputRefreshContext& context)> refresher) {
GuestOutputProperties& writable_properties =
guest_output_properties_[guest_output_mailbox_writable_];
writable_properties.frontbuffer_width = frontbuffer_width;
writable_properties.frontbuffer_height = frontbuffer_height;
writable_properties.screen_width = screen_width;
writable_properties.screen_height = screen_height;
writable_properties.display_aspect_ratio_x = display_aspect_ratio_x;
writable_properties.display_aspect_ratio_y = display_aspect_ratio_y;
writable_properties.is_8bpc = false;
bool is_active = writable_properties.IsActive();
if (is_active) {
@ -706,22 +706,25 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
// All host location calculations are DPI-independent, conceptually depending
// only on the aspect ratios, not the absolute values.
uint32_t output_width, output_height;
if (uint64_t(surface_width_in_paint_connection_) * properties.screen_height >
uint64_t(properties.screen_width) * surface_height_in_paint_connection_) {
if (uint64_t(surface_width_in_paint_connection_) *
properties.display_aspect_ratio_y >
uint64_t(surface_height_in_paint_connection_) *
properties.display_aspect_ratio_x) {
// The window is wider that the source - crop along Y to preserve the aspect
// ratio while stretching throughout the entire surface's width, then limit
// the Y cropping via letterboxing or stretching along X.
uint32_t present_safe_area;
if (cvars::present_safe_area_y > 0 && cvars::present_safe_area_y < 100) {
if (config.GetAllowOverscanCutoff() && cvars::present_safe_area_y > 0 &&
cvars::present_safe_area_y < 100) {
present_safe_area = uint32_t(cvars::present_safe_area_y);
} else {
present_safe_area = 100;
}
// Scale the desired width by the H:W aspect ratio (inverse of W:H) to get
// the height.
output_height =
rescale_unsigned(surface_width_in_paint_connection_,
properties.screen_height, properties.screen_width);
output_height = rescale_unsigned(surface_width_in_paint_connection_,
properties.display_aspect_ratio_y,
properties.display_aspect_ratio_x);
bool letterbox = false;
if (output_height * present_safe_area >
surface_height_in_paint_connection_ * 100) {
@ -732,8 +735,9 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
}
if (letterbox && cvars::present_letterbox) {
output_width = rescale_unsigned(
properties.screen_width, surface_height_in_paint_connection_ * 100,
properties.screen_height * present_safe_area);
surface_height_in_paint_connection_ * 100,
properties.display_aspect_ratio_x,
properties.display_aspect_ratio_y * present_safe_area);
// output_width might have been rounded up already by rescale_unsigned, so
// rounding down in this division.
flow.output_x = (int32_t(surface_width_in_paint_connection_) -
@ -753,15 +757,16 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
// aspect ratio while stretching throughout the entire surface's height,
// then limit the X cropping via letterboxing or stretching along Y.
uint32_t present_safe_area;
if (cvars::present_safe_area_x > 0 && cvars::present_safe_area_x < 100) {
if (config.GetAllowOverscanCutoff() && cvars::present_safe_area_x > 0 &&
cvars::present_safe_area_x < 100) {
present_safe_area = uint32_t(cvars::present_safe_area_x);
} else {
present_safe_area = 100;
}
// Scale the desired height by the W:H aspect ratio to get the width.
output_width =
rescale_unsigned(surface_height_in_paint_connection_,
properties.screen_width, properties.screen_height);
output_width = rescale_unsigned(surface_height_in_paint_connection_,
properties.display_aspect_ratio_x,
properties.display_aspect_ratio_y);
bool letterbox = false;
if (output_width * present_safe_area >
surface_width_in_paint_connection_ * 100) {
@ -772,8 +777,9 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
}
if (letterbox && cvars::present_letterbox) {
output_height = rescale_unsigned(
properties.screen_height, surface_width_in_paint_connection_ * 100,
properties.screen_width * present_safe_area);
surface_width_in_paint_connection_ * 100,
properties.display_aspect_ratio_y,
properties.display_aspect_ratio_x * present_safe_area);
// output_height might have been rounded up already by rescale_unsigned,
// so rounding down in this division.
flow.output_y = (int32_t(surface_height_in_paint_connection_) -

View File

@ -228,6 +228,11 @@ class Presenter {
// In the sharpness setters, min / max with a constant as the first argument
// also drops NaNs.
bool GetAllowOverscanCutoff() const { return allow_overscan_cutoff_; }
void SetAllowOverscanCutoff(bool new_allow_overscan_cutoff) {
allow_overscan_cutoff_ = new_allow_overscan_cutoff;
}
Effect GetEffect() const { return effect_; }
void SetEffect(Effect new_effect) { effect_ = new_effect; }
@ -265,8 +270,10 @@ class Presenter {
void SetDither(bool new_dither) { dither_ = new_dither; }
private:
// Tools, rather than the emulator itself, must use kBilinear as the image
// must be as close to the original front buffer as possible.
// Tools, rather than the emulator itself, must not allow overscan cutoff
// and must use the kBilinear effect as the image must be as close to the
// original front buffer as possible.
bool allow_overscan_cutoff_ = false;
Effect effect_ = Effect::kBilinear;
float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault;
uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax;
@ -299,7 +306,9 @@ class Presenter {
void PaintFromUIThread(bool force_paint = false);
// Pass 0 as width or height to disable guest output until the next refresh
// with an actual size. The callback will receive a backend-specific context,
// with an actual size. The display aspect ratio may be specified like 16:9 or
// like 1280:720, both are accepted, for simplicity, the guest display size
// may just be passed. The callback will receive a backend-specific context,
// and will not be called in case of an error such as the wrong size, or if
// guest output is disabled. Returns whether the callback was called and it
// returned true. The callback must submit all updating work to the host GPU
@ -307,7 +316,7 @@ class Presenter {
// primitives required by the GuestOutputRefreshContext implementation.
bool RefreshGuestOutput(
uint32_t frontbuffer_width, uint32_t frontbuffer_height,
uint32_t screen_width, uint32_t screen_height,
uint32_t display_aspect_ratio_x, uint32_t display_aspect_ratio_y,
std::function<bool(GuestOutputRefreshContext& context)> refresher);
// The implementation must be callable from any thread, including from
// multiple at the same time, and it should acquire the latest guest output
@ -354,24 +363,24 @@ class Presenter {
// this frame.
uint32_t frontbuffer_width;
uint32_t frontbuffer_height;
// Guest screen size (primarily for the target aspect ratio, which may be
// different than that of the frontbuffer).
uint32_t screen_width;
uint32_t screen_height;
// Guest display aspect ratio numerator and denominator (both 16:9 and
// 1280:720 kinds of values are accepted).
uint32_t display_aspect_ratio_x;
uint32_t display_aspect_ratio_y;
bool is_8bpc;
GuestOutputProperties() { SetToInactive(); }
bool IsActive() const {
return frontbuffer_width && frontbuffer_height && screen_width &&
screen_height;
return frontbuffer_width && frontbuffer_height &&
display_aspect_ratio_x && display_aspect_ratio_y;
}
void SetToInactive() {
frontbuffer_width = 0;
frontbuffer_height = 0;
screen_width = 0;
screen_height = 0;
display_aspect_ratio_x = 0;
display_aspect_ratio_y = 0;
is_8bpc = false;
}
};