mirror of
https://github.com/xenia-project/xenia.git
synced 2025-12-06 07:12:03 +01:00
Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental
This commit is contained in:
commit
a8df744ea6
|
|
@ -693,6 +693,7 @@ EmulatorWindow::GetGuestOutputPaintEffectForCvarValue(
|
||||||
ui::Presenter::GuestOutputPaintConfig
|
ui::Presenter::GuestOutputPaintConfig
|
||||||
EmulatorWindow::GetGuestOutputPaintConfigForCvars() {
|
EmulatorWindow::GetGuestOutputPaintConfigForCvars() {
|
||||||
ui::Presenter::GuestOutputPaintConfig paint_config;
|
ui::Presenter::GuestOutputPaintConfig paint_config;
|
||||||
|
paint_config.SetAllowOverscanCutoff(true);
|
||||||
paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue(
|
paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue(
|
||||||
cvars::postprocess_scaling_and_sharpening));
|
cvars::postprocess_scaling_and_sharpening));
|
||||||
paint_config.SetCasAdditionalSharpness(
|
paint_config.SetCasAdditionalSharpness(
|
||||||
|
|
|
||||||
|
|
@ -919,9 +919,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader,
|
||||||
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
|
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
|
||||||
reader->AdvanceRead((count - 4) * sizeof(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_;
|
++counter_;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ class CommandProcessor {
|
||||||
|
|
||||||
virtual void ClearCaches();
|
virtual void ClearCaches();
|
||||||
|
|
||||||
void SetIgnoreSwap(bool ignore_swap) { ignore_swap_ = ignore_swap; }
|
|
||||||
// "Desired" is for the external thread managing the post-processing effect.
|
// "Desired" is for the external thread managing the post-processing effect.
|
||||||
SwapPostEffect GetDesiredSwapPostEffect() const {
|
SwapPostEffect GetDesiredSwapPostEffect() const {
|
||||||
return swap_post_effect_desired_;
|
return swap_post_effect_desired_;
|
||||||
|
|
@ -265,8 +264,6 @@ class CommandProcessor {
|
||||||
std::atomic<bool> worker_running_;
|
std::atomic<bool> worker_running_;
|
||||||
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
||||||
|
|
||||||
bool ignore_swap_ = false;
|
|
||||||
|
|
||||||
std::queue<std::function<void()>> pending_fns_;
|
std::queue<std::function<void()>> pending_fns_;
|
||||||
|
|
||||||
// MicroEngine binary from PM4_ME_INIT
|
// MicroEngine binary from PM4_ME_INIT
|
||||||
|
|
|
||||||
|
|
@ -1722,10 +1722,6 @@ void D3D12CommandProcessor::OnGammaRampPWLValueWritten() {
|
||||||
void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
||||||
uint32_t frontbuffer_width,
|
uint32_t frontbuffer_width,
|
||||||
uint32_t frontbuffer_height) {
|
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");
|
SCOPE_profile_cpu_f("gpu");
|
||||||
|
|
||||||
ui::Presenter* presenter = graphics_system_->presenter();
|
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();
|
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(
|
presenter->RefreshGuestOutput(
|
||||||
uint32_t(swap_texture_desc.Width), uint32_t(swap_texture_desc.Height),
|
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,
|
[this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource,
|
||||||
&swap_texture_desc](
|
&swap_texture_desc](
|
||||||
ui::Presenter::GuestOutputRefreshContext& context) -> bool {
|
ui::Presenter::GuestOutputRefreshContext& context) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -1147,7 +1147,8 @@ bool D3D12TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
|
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());
|
assert_true(IsDrawResolutionScaled());
|
||||||
|
|
||||||
if (length_unscaled == 0) {
|
if (length_unscaled == 0) {
|
||||||
|
|
@ -1162,8 +1163,12 @@ bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
|
||||||
uint32_t draw_resolution_scale_area =
|
uint32_t draw_resolution_scale_area =
|
||||||
draw_resolution_scale_x() * draw_resolution_scale_y();
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
uint64_t first_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
|
uint64_t first_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
|
||||||
uint64_t last_scaled = uint64_t(start_unscaled + (length_unscaled - 1)) *
|
uint64_t length_scaled_alignment_bits =
|
||||||
draw_resolution_scale_area;
|
(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 =
|
const ui::d3d12::D3D12Provider& provider =
|
||||||
command_processor_.GetD3D12Provider();
|
command_processor_.GetD3D12Provider();
|
||||||
|
|
@ -1273,7 +1278,8 @@ bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12TextureCache::MakeScaledResolveRangeCurrent(
|
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());
|
assert_true(IsDrawResolutionScaled());
|
||||||
|
|
||||||
if (!length_unscaled || start_unscaled >= SharedMemory::kBufferSize ||
|
if (!length_unscaled || start_unscaled >= SharedMemory::kBufferSize ||
|
||||||
|
|
@ -1286,8 +1292,12 @@ bool D3D12TextureCache::MakeScaledResolveRangeCurrent(
|
||||||
uint32_t draw_resolution_scale_area =
|
uint32_t draw_resolution_scale_area =
|
||||||
draw_resolution_scale_x() * draw_resolution_scale_y();
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
uint64_t start_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
|
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_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);
|
uint64_t last_scaled = start_scaled + (length_scaled - 1);
|
||||||
|
|
||||||
// Get one or two buffers that can hold the whole range.
|
// 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)) {
|
if (texture_resolution_scaled && (is_base || !scaled_mips_source_set_up)) {
|
||||||
uint32_t guest_size_unscaled = is_base ? d3d12_texture.GetGuestBaseSize()
|
uint32_t guest_size_unscaled = is_base ? d3d12_texture.GetGuestBaseSize()
|
||||||
: d3d12_texture.GetGuestMipsSize();
|
: 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,
|
command_processor_.ReleaseScratchGPUBuffer(copy_buffer,
|
||||||
copy_buffer_state);
|
copy_buffer_state);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -130,14 +130,16 @@ class D3D12TextureCache final : public TextureCache {
|
||||||
uint32_t& scale_x, uint32_t& scale_y,
|
uint32_t& scale_x, uint32_t& scale_y,
|
||||||
const ui::d3d12::D3D12Provider& provider);
|
const ui::d3d12::D3D12Provider& provider);
|
||||||
// Ensures the tiles backing the range in the buffers are allocated.
|
// Ensures the tiles backing the range in the buffers are allocated.
|
||||||
bool EnsureScaledResolveMemoryCommitted(uint32_t start_unscaled,
|
bool EnsureScaledResolveMemoryCommitted(
|
||||||
uint32_t length_unscaled) override;
|
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.
|
// 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
|
// 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
|
// accessible through different buffers based on the range needed, so aliasing
|
||||||
// barriers are required.
|
// barriers are required.
|
||||||
bool MakeScaledResolveRangeCurrent(uint32_t start_unscaled,
|
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
|
// These functions create a view of the range specified in the last successful
|
||||||
// MakeScaledResolveRangeCurrent call because that function must be called
|
// MakeScaledResolveRangeCurrent call because that function must be called
|
||||||
// before this.
|
// before this.
|
||||||
|
|
|
||||||
|
|
@ -3012,31 +3012,43 @@ void DxbcShaderTranslator::CompletePixelShader() {
|
||||||
// checked, but let's assume this means "always", not "less, equal or
|
// checked, but let's assume this means "always", not "less, equal or
|
||||||
// greater".
|
// greater".
|
||||||
// TODO(Triang3l): Check how alpha test works with NaN on Direct3D 9.
|
// 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".
|
// Don't do the test if the mode is "always".
|
||||||
a_.OpIf(true, alpha_test_op_src);
|
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 alpha_src(
|
||||||
dxbc::Src::R(system_temps_color_[0], dxbc::Src::kWWWW));
|
dxbc::Src::R(system_temps_color_[0], dxbc::Src::kWWWW));
|
||||||
dxbc::Src alpha_test_reference_src(LoadSystemConstant(
|
dxbc::Src alpha_test_reference_src(LoadSystemConstant(
|
||||||
SystemConstants::Index::kAlphaTestReference,
|
SystemConstants::Index::kAlphaTestReference,
|
||||||
offsetof(SystemConstants, alpha_test_reference), dxbc::Src::kXXXX));
|
offsetof(SystemConstants, alpha_test_reference), dxbc::Src::kXXXX));
|
||||||
// Less than.
|
// Handle "not equal" specially (specifically as "not equal" so it's true
|
||||||
a_.OpLT(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
|
// for NaN, not "less or greater" which is false for NaN).
|
||||||
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
|
a_.OpIEq(alpha_test_op_dest, alpha_test_mask_src,
|
||||||
dxbc::Src::LU(~uint32_t(1 << 0)));
|
dxbc::Src::LU(uint32_t(xenos::CompareFunction::kNotEqual)));
|
||||||
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
|
a_.OpIf(true, alpha_test_op_src);
|
||||||
// Equals to.
|
{ a_.OpNE(alpha_test_mask_dest, alpha_src, alpha_test_reference_src); }
|
||||||
a_.OpEq(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
|
a_.OpElse();
|
||||||
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
|
{
|
||||||
dxbc::Src::LU(~uint32_t(1 << 1)));
|
// Less than.
|
||||||
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
|
a_.OpLT(alpha_test_op_dest, alpha_src, alpha_test_reference_src);
|
||||||
// Greater than.
|
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
|
||||||
a_.OpLT(alpha_test_op_dest, alpha_test_reference_src, alpha_src);
|
dxbc::Src::LU(~uint32_t(1 << 0)));
|
||||||
a_.OpOr(alpha_test_op_dest, alpha_test_op_src,
|
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
|
||||||
dxbc::Src::LU(~uint32_t(1 << 2)));
|
// Equals to.
|
||||||
a_.OpAnd(alpha_test_mask_dest, alpha_test_mask_src, alpha_test_op_src);
|
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.
|
// Discard the pixel if it has failed the test.
|
||||||
if (edram_rov_used_) {
|
if (edram_rov_used_) {
|
||||||
a_.OpRetC(false, alpha_test_mask_src);
|
a_.OpRetC(false, alpha_test_mask_src);
|
||||||
|
|
|
||||||
|
|
@ -656,6 +656,13 @@ bool TextureCache::LoadTextureData(Texture& texture) {
|
||||||
|
|
||||||
TextureKey texture_key = texture.key();
|
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.
|
// Request uploading of the texture data to the shared memory.
|
||||||
// This is also necessary when resolution scaling is used - the texture cache
|
// This is also necessary when resolution scaling is used - the texture cache
|
||||||
// relies on shared memory for invalidation of both unscaled and scaled
|
// 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();
|
bool base_resolved = texture.GetBaseResolved();
|
||||||
if (base_outdated) {
|
if (base_outdated) {
|
||||||
if (!shared_memory().RequestRange(
|
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)) {
|
texture_key.scaled_resolve ? nullptr : &base_resolved)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -674,7 +682,8 @@ bool TextureCache::LoadTextureData(Texture& texture) {
|
||||||
bool mips_resolved = texture.GetMipsResolved();
|
bool mips_resolved = texture.GetMipsResolved();
|
||||||
if (mips_outdated) {
|
if (mips_outdated) {
|
||||||
if (!shared_memory().RequestRange(
|
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)) {
|
texture_key.scaled_resolve ? nullptr : &mips_resolved)) {
|
||||||
return false;
|
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
|
// by an actual resolve, but is still included in the texture size, so the
|
||||||
// GPU won't be trying to access unmapped memory.
|
// GPU won't be trying to access unmapped memory.
|
||||||
if (!EnsureScaledResolveMemoryCommitted(texture_key.base_page << 12,
|
if (!EnsureScaledResolveMemoryCommitted(texture_key.base_page << 12,
|
||||||
texture.GetGuestBaseSize())) {
|
texture.GetGuestBaseSize(), 4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!EnsureScaledResolveMemoryCommitted(texture_key.mip_page << 12,
|
if (!EnsureScaledResolveMemoryCommitted(texture_key.mip_page << 12,
|
||||||
texture.GetGuestMipsSize())) {
|
texture.GetGuestMipsSize(), 4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,9 @@ class TextureCache {
|
||||||
void MarkRangeAsResolved(uint32_t start_unscaled, uint32_t length_unscaled);
|
void MarkRangeAsResolved(uint32_t start_unscaled, uint32_t length_unscaled);
|
||||||
// Ensures the memory backing the range in the scaled resolve address space is
|
// Ensures the memory backing the range in the scaled resolve address space is
|
||||||
// allocated and returns whether it is.
|
// allocated and returns whether it is.
|
||||||
virtual bool EnsureScaledResolveMemoryCommitted(uint32_t start_unscaled,
|
virtual bool EnsureScaledResolveMemoryCommitted(
|
||||||
uint32_t length_unscaled) {
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
uint32_t length_scaled_alignment_log2 = 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,19 +89,15 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode,
|
TracePlaybackMode playback_mode,
|
||||||
bool clear_caches) {
|
bool clear_caches) {
|
||||||
playing_trace_ = true;
|
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([=]() {
|
graphics_system_->command_processor()->CallInThread([=]() {
|
||||||
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches,
|
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
|
||||||
present_last_copy);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
size_t trace_size,
|
size_t trace_size,
|
||||||
TracePlaybackMode playback_mode,
|
TracePlaybackMode playback_mode,
|
||||||
bool clear_caches, bool present_last_copy) {
|
bool clear_caches) {
|
||||||
auto memory = graphics_system_->memory();
|
auto memory = graphics_system_->memory();
|
||||||
auto command_processor = graphics_system_->command_processor();
|
auto command_processor = graphics_system_->command_processor();
|
||||||
|
|
||||||
|
|
@ -109,10 +105,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
command_processor->ClearCaches();
|
command_processor->ClearCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (present_last_copy) {
|
|
||||||
command_processor->SetIgnoreSwap(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
playback_percent_ = 0;
|
playback_percent_ = 0;
|
||||||
auto trace_end = trace_data + trace_size;
|
auto trace_end = trace_data + trace_size;
|
||||||
|
|
||||||
|
|
@ -252,11 +244,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
|
|
||||||
playing_trace_ = false;
|
playing_trace_ = false;
|
||||||
|
|
||||||
if (present_last_copy) {
|
|
||||||
command_processor->SetIgnoreSwap(false);
|
|
||||||
command_processor->IssueSwap(0, 1280, 720);
|
|
||||||
}
|
|
||||||
|
|
||||||
playback_event_->Set();
|
playback_event_->Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,6 @@ class TracePlayer : public TraceReader {
|
||||||
TracePlayer(GraphicsSystem* graphics_system);
|
TracePlayer(GraphicsSystem* graphics_system);
|
||||||
|
|
||||||
GraphicsSystem* graphics_system() const { return 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_frame_index() const { return current_frame_index_; }
|
||||||
int current_command_index() const { return current_command_index_; }
|
int current_command_index() const { return current_command_index_; }
|
||||||
bool is_playing_trace() const { return playing_trace_; }
|
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,
|
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode, bool clear_caches);
|
TracePlaybackMode playback_mode, bool clear_caches);
|
||||||
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
|
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode, bool clear_caches,
|
TracePlaybackMode playback_mode, bool clear_caches);
|
||||||
bool present_last_copy);
|
|
||||||
|
|
||||||
GraphicsSystem* graphics_system_;
|
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_frame_index_;
|
||||||
int current_command_index_;
|
int current_command_index_;
|
||||||
bool playing_trace_ = false;
|
bool playing_trace_ = false;
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ bool TraceViewer::Setup() {
|
||||||
// Main display window.
|
// Main display window.
|
||||||
assert_true(app_context().IsInUIThread());
|
assert_true(app_context().IsInUIThread());
|
||||||
window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer",
|
window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer",
|
||||||
1920, 1200);
|
1920, 1080);
|
||||||
window_->AddListener(&window_listener_);
|
window_->AddListener(&window_listener_);
|
||||||
window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput);
|
window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput);
|
||||||
if (!window_->Open()) {
|
if (!window_->Open()) {
|
||||||
|
|
@ -135,28 +135,27 @@ bool TraceViewer::Setup() {
|
||||||
graphics_system_ = emulator_->graphics_system();
|
graphics_system_ = emulator_->graphics_system();
|
||||||
|
|
||||||
player_ = std::make_unique<TracePlayer>(graphics_system_);
|
player_ = std::make_unique<TracePlayer>(graphics_system_);
|
||||||
player_->SetPresentLastCopy(true);
|
|
||||||
|
|
||||||
// Setup drawing to the window.
|
// Setup drawing to the window.
|
||||||
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
|
ui::Presenter* presenter = graphics_system_->presenter();
|
||||||
presenter_ = graphics_provider.CreatePresenter();
|
if (!presenter) {
|
||||||
if (!presenter_) {
|
|
||||||
XELOGE("Failed to initialize the presenter");
|
XELOGE("Failed to initialize the presenter");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
|
||||||
immediate_drawer_ = graphics_provider.CreateImmediateDrawer();
|
immediate_drawer_ = graphics_provider.CreateImmediateDrawer();
|
||||||
if (!immediate_drawer_) {
|
if (!immediate_drawer_) {
|
||||||
XELOGE("Failed to initialize the immediate drawer");
|
XELOGE("Failed to initialize the immediate drawer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
immediate_drawer_->SetPresenter(presenter_.get());
|
immediate_drawer_->SetPresenter(presenter);
|
||||||
imgui_drawer_ =
|
imgui_drawer_ =
|
||||||
std::make_unique<xe::ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
|
std::make_unique<xe::ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
|
||||||
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(),
|
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter,
|
||||||
immediate_drawer_.get());
|
immediate_drawer_.get());
|
||||||
trace_viewer_dialog_ = std::unique_ptr<TraceViewerDialog>(
|
trace_viewer_dialog_ = std::unique_ptr<TraceViewerDialog>(
|
||||||
new TraceViewerDialog(imgui_drawer_.get(), *this));
|
new TraceViewerDialog(imgui_drawer_.get(), *this));
|
||||||
window_->SetPresenter(presenter_.get());
|
window_->SetPresenter(presenter);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@
|
||||||
#include "xenia/ui/imgui_dialog.h"
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/imgui_drawer.h"
|
#include "xenia/ui/imgui_drawer.h"
|
||||||
#include "xenia/ui/immediate_drawer.h"
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
#include "xenia/ui/presenter.h"
|
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/ui/window_listener.h"
|
#include "xenia/ui/window_listener.h"
|
||||||
#include "xenia/ui/windowed_app.h"
|
#include "xenia/ui/windowed_app.h"
|
||||||
|
|
@ -129,7 +128,6 @@ class TraceViewer : public xe::ui::WindowedApp {
|
||||||
GraphicsSystem* graphics_system_ = nullptr;
|
GraphicsSystem* graphics_system_ = nullptr;
|
||||||
std::unique_ptr<TracePlayer> player_;
|
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::ImmediateDrawer> immediate_drawer_;
|
||||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
||||||
std::unique_ptr<TraceViewerDialog> trace_viewer_dialog_;
|
std::unique_ptr<TraceViewerDialog> trace_viewer_dialog_;
|
||||||
|
|
|
||||||
|
|
@ -269,11 +269,6 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frontbuffer_ptr) {
|
|
||||||
// Trace viewer does this.
|
|
||||||
frontbuffer_ptr = last_copy_base_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkCommandBuffer> submit_buffers;
|
std::vector<VkCommandBuffer> submit_buffers;
|
||||||
if (frame_open_) {
|
if (frame_open_) {
|
||||||
// TODO(DrChat): If the setup buffer is empty, don't bother queueing it up.
|
// 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_;
|
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_) {
|
if (!frame_open_) {
|
||||||
BeginFrame();
|
BeginFrame();
|
||||||
} else if (current_render_state_) {
|
} else if (current_render_state_) {
|
||||||
|
|
|
||||||
|
|
@ -106,9 +106,6 @@ class VulkanCommandProcessor : public CommandProcessor {
|
||||||
uint32_t coher_base_vc_ = 0;
|
uint32_t coher_base_vc_ = 0;
|
||||||
uint32_t coher_size_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 capturing_ = false;
|
||||||
bool trace_requested_ = false;
|
bool trace_requested_ = false;
|
||||||
bool cache_clear_requested_ = false;
|
bool cache_clear_requested_ = false;
|
||||||
|
|
|
||||||
|
|
@ -354,14 +354,14 @@ void Presenter::PaintFromUIThread(bool force_paint) {
|
||||||
|
|
||||||
bool Presenter::RefreshGuestOutput(
|
bool Presenter::RefreshGuestOutput(
|
||||||
uint32_t frontbuffer_width, uint32_t frontbuffer_height,
|
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) {
|
std::function<bool(GuestOutputRefreshContext& context)> refresher) {
|
||||||
GuestOutputProperties& writable_properties =
|
GuestOutputProperties& writable_properties =
|
||||||
guest_output_properties_[guest_output_mailbox_writable_];
|
guest_output_properties_[guest_output_mailbox_writable_];
|
||||||
writable_properties.frontbuffer_width = frontbuffer_width;
|
writable_properties.frontbuffer_width = frontbuffer_width;
|
||||||
writable_properties.frontbuffer_height = frontbuffer_height;
|
writable_properties.frontbuffer_height = frontbuffer_height;
|
||||||
writable_properties.screen_width = screen_width;
|
writable_properties.display_aspect_ratio_x = display_aspect_ratio_x;
|
||||||
writable_properties.screen_height = screen_height;
|
writable_properties.display_aspect_ratio_y = display_aspect_ratio_y;
|
||||||
writable_properties.is_8bpc = false;
|
writable_properties.is_8bpc = false;
|
||||||
bool is_active = writable_properties.IsActive();
|
bool is_active = writable_properties.IsActive();
|
||||||
if (is_active) {
|
if (is_active) {
|
||||||
|
|
@ -706,22 +706,25 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
|
||||||
// All host location calculations are DPI-independent, conceptually depending
|
// All host location calculations are DPI-independent, conceptually depending
|
||||||
// only on the aspect ratios, not the absolute values.
|
// only on the aspect ratios, not the absolute values.
|
||||||
uint32_t output_width, output_height;
|
uint32_t output_width, output_height;
|
||||||
if (uint64_t(surface_width_in_paint_connection_) * properties.screen_height >
|
if (uint64_t(surface_width_in_paint_connection_) *
|
||||||
uint64_t(properties.screen_width) * surface_height_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
|
// 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
|
// ratio while stretching throughout the entire surface's width, then limit
|
||||||
// the Y cropping via letterboxing or stretching along X.
|
// the Y cropping via letterboxing or stretching along X.
|
||||||
uint32_t present_safe_area;
|
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);
|
present_safe_area = uint32_t(cvars::present_safe_area_y);
|
||||||
} else {
|
} else {
|
||||||
present_safe_area = 100;
|
present_safe_area = 100;
|
||||||
}
|
}
|
||||||
// Scale the desired width by the H:W aspect ratio (inverse of W:H) to get
|
// Scale the desired width by the H:W aspect ratio (inverse of W:H) to get
|
||||||
// the height.
|
// the height.
|
||||||
output_height =
|
output_height = rescale_unsigned(surface_width_in_paint_connection_,
|
||||||
rescale_unsigned(surface_width_in_paint_connection_,
|
properties.display_aspect_ratio_y,
|
||||||
properties.screen_height, properties.screen_width);
|
properties.display_aspect_ratio_x);
|
||||||
bool letterbox = false;
|
bool letterbox = false;
|
||||||
if (output_height * present_safe_area >
|
if (output_height * present_safe_area >
|
||||||
surface_height_in_paint_connection_ * 100) {
|
surface_height_in_paint_connection_ * 100) {
|
||||||
|
|
@ -732,8 +735,9 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
|
||||||
}
|
}
|
||||||
if (letterbox && cvars::present_letterbox) {
|
if (letterbox && cvars::present_letterbox) {
|
||||||
output_width = rescale_unsigned(
|
output_width = rescale_unsigned(
|
||||||
properties.screen_width, surface_height_in_paint_connection_ * 100,
|
surface_height_in_paint_connection_ * 100,
|
||||||
properties.screen_height * present_safe_area);
|
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
|
// output_width might have been rounded up already by rescale_unsigned, so
|
||||||
// rounding down in this division.
|
// rounding down in this division.
|
||||||
flow.output_x = (int32_t(surface_width_in_paint_connection_) -
|
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,
|
// aspect ratio while stretching throughout the entire surface's height,
|
||||||
// then limit the X cropping via letterboxing or stretching along Y.
|
// then limit the X cropping via letterboxing or stretching along Y.
|
||||||
uint32_t present_safe_area;
|
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);
|
present_safe_area = uint32_t(cvars::present_safe_area_x);
|
||||||
} else {
|
} else {
|
||||||
present_safe_area = 100;
|
present_safe_area = 100;
|
||||||
}
|
}
|
||||||
// Scale the desired height by the W:H aspect ratio to get the width.
|
// Scale the desired height by the W:H aspect ratio to get the width.
|
||||||
output_width =
|
output_width = rescale_unsigned(surface_height_in_paint_connection_,
|
||||||
rescale_unsigned(surface_height_in_paint_connection_,
|
properties.display_aspect_ratio_x,
|
||||||
properties.screen_width, properties.screen_height);
|
properties.display_aspect_ratio_y);
|
||||||
bool letterbox = false;
|
bool letterbox = false;
|
||||||
if (output_width * present_safe_area >
|
if (output_width * present_safe_area >
|
||||||
surface_width_in_paint_connection_ * 100) {
|
surface_width_in_paint_connection_ * 100) {
|
||||||
|
|
@ -772,8 +777,9 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
|
||||||
}
|
}
|
||||||
if (letterbox && cvars::present_letterbox) {
|
if (letterbox && cvars::present_letterbox) {
|
||||||
output_height = rescale_unsigned(
|
output_height = rescale_unsigned(
|
||||||
properties.screen_height, surface_width_in_paint_connection_ * 100,
|
surface_width_in_paint_connection_ * 100,
|
||||||
properties.screen_width * present_safe_area);
|
properties.display_aspect_ratio_y,
|
||||||
|
properties.display_aspect_ratio_x * present_safe_area);
|
||||||
// output_height might have been rounded up already by rescale_unsigned,
|
// output_height might have been rounded up already by rescale_unsigned,
|
||||||
// so rounding down in this division.
|
// so rounding down in this division.
|
||||||
flow.output_y = (int32_t(surface_height_in_paint_connection_) -
|
flow.output_y = (int32_t(surface_height_in_paint_connection_) -
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,11 @@ class Presenter {
|
||||||
// In the sharpness setters, min / max with a constant as the first argument
|
// In the sharpness setters, min / max with a constant as the first argument
|
||||||
// also drops NaNs.
|
// 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_; }
|
Effect GetEffect() const { return effect_; }
|
||||||
void SetEffect(Effect new_effect) { effect_ = new_effect; }
|
void SetEffect(Effect new_effect) { effect_ = new_effect; }
|
||||||
|
|
||||||
|
|
@ -265,8 +270,10 @@ class Presenter {
|
||||||
void SetDither(bool new_dither) { dither_ = new_dither; }
|
void SetDither(bool new_dither) { dither_ = new_dither; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Tools, rather than the emulator itself, must use kBilinear as the image
|
// Tools, rather than the emulator itself, must not allow overscan cutoff
|
||||||
// must be as close to the original front buffer as possible.
|
// 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;
|
Effect effect_ = Effect::kBilinear;
|
||||||
float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault;
|
float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault;
|
||||||
uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax;
|
uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax;
|
||||||
|
|
@ -299,7 +306,9 @@ class Presenter {
|
||||||
void PaintFromUIThread(bool force_paint = false);
|
void PaintFromUIThread(bool force_paint = false);
|
||||||
|
|
||||||
// Pass 0 as width or height to disable guest output until the next refresh
|
// 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
|
// 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
|
// 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
|
// 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.
|
// primitives required by the GuestOutputRefreshContext implementation.
|
||||||
bool RefreshGuestOutput(
|
bool RefreshGuestOutput(
|
||||||
uint32_t frontbuffer_width, uint32_t frontbuffer_height,
|
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);
|
std::function<bool(GuestOutputRefreshContext& context)> refresher);
|
||||||
// The implementation must be callable from any thread, including from
|
// The implementation must be callable from any thread, including from
|
||||||
// multiple at the same time, and it should acquire the latest guest output
|
// multiple at the same time, and it should acquire the latest guest output
|
||||||
|
|
@ -354,24 +363,24 @@ class Presenter {
|
||||||
// this frame.
|
// this frame.
|
||||||
uint32_t frontbuffer_width;
|
uint32_t frontbuffer_width;
|
||||||
uint32_t frontbuffer_height;
|
uint32_t frontbuffer_height;
|
||||||
// Guest screen size (primarily for the target aspect ratio, which may be
|
// Guest display aspect ratio numerator and denominator (both 16:9 and
|
||||||
// different than that of the frontbuffer).
|
// 1280:720 kinds of values are accepted).
|
||||||
uint32_t screen_width;
|
uint32_t display_aspect_ratio_x;
|
||||||
uint32_t screen_height;
|
uint32_t display_aspect_ratio_y;
|
||||||
bool is_8bpc;
|
bool is_8bpc;
|
||||||
|
|
||||||
GuestOutputProperties() { SetToInactive(); }
|
GuestOutputProperties() { SetToInactive(); }
|
||||||
|
|
||||||
bool IsActive() const {
|
bool IsActive() const {
|
||||||
return frontbuffer_width && frontbuffer_height && screen_width &&
|
return frontbuffer_width && frontbuffer_height &&
|
||||||
screen_height;
|
display_aspect_ratio_x && display_aspect_ratio_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetToInactive() {
|
void SetToInactive() {
|
||||||
frontbuffer_width = 0;
|
frontbuffer_width = 0;
|
||||||
frontbuffer_height = 0;
|
frontbuffer_height = 0;
|
||||||
screen_width = 0;
|
display_aspect_ratio_x = 0;
|
||||||
screen_height = 0;
|
display_aspect_ratio_y = 0;
|
||||||
is_8bpc = false;
|
is_8bpc = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue