mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-06 14:09:58 +00:00
Framerate display corrections
This commit is contained in:
@@ -64,8 +64,8 @@ void EmuWindow_Android::OnTouchReleased(int id) {
|
|||||||
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
|
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::OnFrameDisplayed() {
|
void EmuWindow_Android::OnFrameDisplayed(u32 presented_frames) {
|
||||||
UpdateObservedFrameRate();
|
UpdateObservedFrameRate(presented_frames);
|
||||||
UpdateFrameRateHint();
|
UpdateFrameRateHint();
|
||||||
|
|
||||||
if (!m_first_frame) {
|
if (!m_first_frame) {
|
||||||
@@ -75,15 +75,16 @@ void EmuWindow_Android::OnFrameDisplayed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_Android::UpdateObservedFrameRate() {
|
void EmuWindow_Android::UpdateObservedFrameRate(u32 presented_frames) {
|
||||||
|
m_presented_frames = static_cast<float>((std::max)(presented_frames, 1u));
|
||||||
const auto now = Clock::now();
|
const auto now = Clock::now();
|
||||||
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
|
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
|
||||||
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
|
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
|
||||||
const float seconds = frame_time.count();
|
const float seconds = frame_time.count();
|
||||||
if (seconds > 0.0f) {
|
if (seconds > 0.0f) {
|
||||||
const float instantaneous_rate = 1.0f / seconds;
|
const float instantaneous_rate = m_presented_frames / seconds;
|
||||||
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
|
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
|
||||||
instantaneous_rate <= 240.0f) {
|
instantaneous_rate <= 240.0f * m_presented_frames) {
|
||||||
constexpr float SmoothingFactor = 0.15f;
|
constexpr float SmoothingFactor = 0.15f;
|
||||||
if (m_smoothed_present_rate <= 0.0f) {
|
if (m_smoothed_present_rate <= 0.0f) {
|
||||||
m_smoothed_present_rate = instantaneous_rate;
|
m_smoothed_present_rate = instantaneous_rate;
|
||||||
@@ -124,18 +125,9 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
|
|||||||
return QuantizeFrameRateHint(verified_rate);
|
return QuantizeFrameRateHint(verified_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
float EmuWindow_Android::GetPresentedFrameMultiplier() {
|
|
||||||
if (!Settings::values.frame_gen.GetValue()) {
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
float EmuWindow_Android::GetFrameRateHint() const {
|
float EmuWindow_Android::GetFrameRateHint() const {
|
||||||
const float presented_multiplier = GetPresentedFrameMultiplier();
|
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
|
||||||
const float observed_rate =
|
const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * m_presented_frames;
|
||||||
std::clamp(m_smoothed_present_rate * presented_multiplier, 0.0f, 240.0f);
|
|
||||||
const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * presented_multiplier;
|
|
||||||
|
|
||||||
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
|
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
|
||||||
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
|
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
|
||||||
@@ -159,7 +151,7 @@ float EmuWindow_Android::GetFrameRateHint() const {
|
|||||||
return frame_time_verified_hint;
|
return frame_time_verified_hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float nominal_rate = 60.0f * presented_multiplier;
|
const float nominal_rate = 60.0f * m_presented_frames;
|
||||||
if (!Settings::values.use_speed_limit.GetValue()) {
|
if (!Settings::values.use_speed_limit.GetValue()) {
|
||||||
return QuantizeFrameRateHint(nominal_rate);
|
return QuantizeFrameRateHint(nominal_rate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
~EmuWindow_Android() = default;
|
~EmuWindow_Android() = default;
|
||||||
|
|
||||||
void OnSurfaceChanged(ANativeWindow* surface);
|
void OnSurfaceChanged(ANativeWindow* surface);
|
||||||
void OnFrameDisplayed() override;
|
void OnFrameDisplayed(u32 presented_frames) override;
|
||||||
|
|
||||||
void OnTouchPressed(int id, float x, float y);
|
void OnTouchPressed(int id, float x, float y);
|
||||||
void OnTouchMoved(int id, float x, float y);
|
void OnTouchMoved(int id, float x, float y);
|
||||||
@@ -58,10 +58,9 @@ private:
|
|||||||
using Clock = std::chrono::steady_clock;
|
using Clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
void UpdateFrameRateHint();
|
void UpdateFrameRateHint();
|
||||||
void UpdateObservedFrameRate();
|
void UpdateObservedFrameRate(u32 presented_frames);
|
||||||
[[nodiscard]] float GetFrameRateHint() const;
|
[[nodiscard]] float GetFrameRateHint() const;
|
||||||
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
|
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
|
||||||
[[nodiscard]] static float GetPresentedFrameMultiplier();
|
|
||||||
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
|
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
|
||||||
|
|
||||||
float m_window_width{};
|
float m_window_width{};
|
||||||
@@ -73,6 +72,7 @@ private:
|
|||||||
float m_last_frame_rate_hint = -1.0f;
|
float m_last_frame_rate_hint = -1.0f;
|
||||||
float m_pending_frame_rate_hint = -1.0f;
|
float m_pending_frame_rate_hint = -1.0f;
|
||||||
float m_smoothed_present_rate = 0.0f;
|
float m_smoothed_present_rate = 0.0f;
|
||||||
|
float m_presented_frames = 1.0f;
|
||||||
Clock::time_point m_last_frame_display_time{};
|
Clock::time_point m_last_frame_display_time{};
|
||||||
Clock::time_point m_pending_frame_rate_since{};
|
Clock::time_point m_pending_frame_rate_since{};
|
||||||
std::uint32_t m_pending_frame_rate_hint_votes = 0;
|
std::uint32_t m_pending_frame_rate_hint_votes = 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
@@ -74,7 +74,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Called from GPU thread when a frame is displayed.
|
/// Called from GPU thread when a frame is displayed.
|
||||||
virtual void OnFrameDisplayed() {}
|
virtual void OnFrameDisplayed([[maybe_unused]] u32 presented_frames) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
@@ -15,74 +17,58 @@ class UsageTracker {
|
|||||||
// PAGE_SHIFT is a macro on FreeBSD
|
// PAGE_SHIFT is a macro on FreeBSD
|
||||||
static constexpr size_t BUFFER_BYTES_PER_BITSHIFT = 6;
|
static constexpr size_t BUFFER_BYTES_PER_BITSHIFT = 6;
|
||||||
static constexpr size_t BUFFER_PAGE_SHIFT = 6 + BUFFER_BYTES_PER_BITSHIFT;
|
static constexpr size_t BUFFER_PAGE_SHIFT = 6 + BUFFER_BYTES_PER_BITSHIFT;
|
||||||
static constexpr size_t BUFFER_PAGE_BYTES = 1 << BUFFER_PAGE_SHIFT;
|
static constexpr u64 BUFFER_PAGE_BYTES = u64{1} << BUFFER_PAGE_SHIFT;
|
||||||
|
|
||||||
|
struct Page {
|
||||||
|
u64 bits;
|
||||||
|
u64 tick;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit UsageTracker(size_t size) {
|
explicit UsageTracker(size_t size) : pages((size >> BUFFER_PAGE_SHIFT) + 1) {}
|
||||||
const size_t num_pages = (size >> BUFFER_PAGE_SHIFT) + 1;
|
|
||||||
pages.resize(num_pages, 0ULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() noexcept {
|
void Track(u64 offset, u64 size, u64 tick, u64 gpu_tick) noexcept {
|
||||||
std::ranges::fill(pages, 0ULL);
|
const u64 end = offset + size;
|
||||||
}
|
if (size == 0 || ((end - 1) >> BUFFER_PAGE_SHIFT) >= pages.size()) {
|
||||||
|
|
||||||
void Track(u64 offset, u64 size) noexcept {
|
|
||||||
const size_t page = offset >> BUFFER_PAGE_SHIFT;
|
|
||||||
const size_t page_end = (offset + size) >> BUFFER_PAGE_SHIFT;
|
|
||||||
if (page_end < page || page_end >= pages.size()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TrackPage(page, offset, size);
|
for (u64 page = offset >> BUFFER_PAGE_SHIFT; page <= (end - 1) >> BUFFER_PAGE_SHIFT;
|
||||||
if (page == page_end) {
|
++page) {
|
||||||
return;
|
Page& entry = pages[page];
|
||||||
|
if (entry.tick <= gpu_tick) {
|
||||||
|
entry.bits = 0;
|
||||||
|
}
|
||||||
|
entry.bits |= PageMask(page, offset, end);
|
||||||
|
entry.tick = (std::max)(entry.tick, tick);
|
||||||
}
|
}
|
||||||
for (size_t i = page + 1; i < page_end; i++) {
|
|
||||||
pages[i] = ~u64{0};
|
|
||||||
}
|
|
||||||
const size_t offset_end = offset + size;
|
|
||||||
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, BUFFER_PAGE_BYTES);
|
|
||||||
TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept {
|
[[nodiscard]] bool IsUsed(u64 offset, u64 size, u64 gpu_tick) const noexcept {
|
||||||
const size_t page = offset >> BUFFER_PAGE_SHIFT;
|
const u64 end = offset + size;
|
||||||
const size_t page_end = (offset + size) >> BUFFER_PAGE_SHIFT;
|
if (size == 0 || ((end - 1) >> BUFFER_PAGE_SHIFT) >= pages.size()) {
|
||||||
if (page_end < page || page_end >= pages.size()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (IsPageUsed(page, offset, size)) {
|
for (u64 page = offset >> BUFFER_PAGE_SHIFT; page <= (end - 1) >> BUFFER_PAGE_SHIFT;
|
||||||
return true;
|
++page) {
|
||||||
}
|
const Page& entry = pages[page];
|
||||||
for (size_t i = page + 1; i < page_end; i++) {
|
if (entry.tick > gpu_tick && (entry.bits & PageMask(page, offset, end)) != 0) {
|
||||||
if (pages[i] != 0) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const size_t offset_end = offset + size;
|
return false;
|
||||||
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, BUFFER_PAGE_BYTES);
|
|
||||||
return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void TrackPage(u64 page, u64 offset, u64 size) noexcept {
|
[[nodiscard]] static u64 PageMask(u64 page, u64 offset, u64 end) noexcept {
|
||||||
const size_t offset_in_page = offset % BUFFER_PAGE_BYTES;
|
const u64 page_begin = page << BUFFER_PAGE_SHIFT;
|
||||||
const size_t first_bit = offset_in_page >> BUFFER_BYTES_PER_BITSHIFT;
|
const u64 first =
|
||||||
const size_t num_bits = std::min<size_t>(size, BUFFER_PAGE_BYTES) >> BUFFER_BYTES_PER_BITSHIFT;
|
((std::max)(offset, page_begin) - page_begin) >> BUFFER_BYTES_PER_BITSHIFT;
|
||||||
const size_t mask = ~u64{0} >> (64 - num_bits);
|
const u64 last = ((std::min)(end, page_begin + BUFFER_PAGE_BYTES) - 1 - page_begin) >>
|
||||||
pages[page] |= (~u64{0} & mask) << first_bit;
|
BUFFER_BYTES_PER_BITSHIFT;
|
||||||
|
return (~u64{0} >> (63 - last)) & (~u64{0} << first);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept {
|
std::vector<Page> pages;
|
||||||
const size_t offset_in_page = offset % BUFFER_PAGE_BYTES;
|
|
||||||
const size_t first_bit = offset_in_page >> BUFFER_BYTES_PER_BITSHIFT;
|
|
||||||
const size_t num_bits = std::min<size_t>(size, BUFFER_PAGE_BYTES) >> BUFFER_BYTES_PER_BITSHIFT;
|
|
||||||
const size_t mask = ~u64{0} >> (64 - num_bits);
|
|
||||||
const size_t mask2 = (~u64{0} & mask) << first_bit;
|
|
||||||
return (pages[page] & mask2) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<u64> pages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -20,7 +23,7 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_gpu.RendererFrameEndNotify();
|
m_gpu.RendererFrameEndNotify();
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> RendererNull::GetAppletCaptureBuffer() {
|
std::vector<u8> RendererNull::GetAppletCaptureBuffer() {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
|||||||
rasterizer.TickFrame();
|
rasterizer.TickFrame();
|
||||||
|
|
||||||
context->SwapBuffers();
|
context->SwapBuffers();
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::AddTelemetryFields() {
|
void RendererOpenGL::AddTelemetryFields() {
|
||||||
|
|||||||
@@ -185,8 +185,9 @@ RendererVulkan::~RendererVulkan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||||
|
u32 pace_index = 0;
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed(pace_index + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderAppletCaptureLayer(framebuffers);
|
RenderAppletCaptureLayer(framebuffers);
|
||||||
@@ -204,7 +205,6 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
|||||||
present_manager.SwapchainImageCount(),
|
present_manager.SwapchainImageCount(),
|
||||||
swapchain.GetImageViewFormat());
|
swapchain.GetImageViewFormat());
|
||||||
|
|
||||||
u32 pace_index = 0;
|
|
||||||
std::chrono::nanoseconds pace_step{};
|
std::chrono::nanoseconds pace_step{};
|
||||||
#ifdef HAS_LSFG
|
#ifdef HAS_LSFG
|
||||||
void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames()));
|
void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames()));
|
||||||
@@ -229,7 +229,7 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
|||||||
output.View());
|
output.View());
|
||||||
scheduler.Flush(*generated->render_ready);
|
scheduler.Flush(*generated->render_ready);
|
||||||
present_manager.Present(generated, static_cast<u32>(generation), generated_step);
|
present_manager.Present(generated, static_cast<u32>(generation), generated_step);
|
||||||
pace_index = static_cast<u32>(generated_frames);
|
pace_index = static_cast<u32>(generation + 1);
|
||||||
pace_step = generated_step;
|
pace_step = generated_step;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
|
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
|
||||||
: VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096} {
|
: VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096},
|
||||||
|
uploads{4096} {
|
||||||
if (runtime.device.HasNullDescriptor()) {
|
if (runtime.device.HasNullDescriptor()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_,
|
|||||||
scheduler{&runtime.scheduler},
|
scheduler{&runtime.scheduler},
|
||||||
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes(),
|
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes(),
|
||||||
runtime.SparseAlignmentFor(sparse_compatible_))},
|
runtime.SparseAlignmentFor(sparse_compatible_))},
|
||||||
tracker{SizeBytes()} {
|
tracker{SizeBytes()}, uploads{SizeBytes()} {
|
||||||
sparse_compatible = sparse_compatible_;
|
sparse_compatible = sparse_compatible_;
|
||||||
if (runtime.device.HasDebuggingToolAttached()) {
|
if (runtime.device.HasDebuggingToolAttached()) {
|
||||||
buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str());
|
buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str());
|
||||||
@@ -137,13 +138,25 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::MarkUsage(u64 offset, u64 size) noexcept {
|
bool Buffer::IsRegionUsed(u64 offset, u64 size) const noexcept {
|
||||||
tracker.Track(offset, size);
|
return tracker.IsUsed(offset, size, scheduler->GetMasterSemaphore().KnownGpuTick());
|
||||||
last_usage_tick = scheduler->CurrentTick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::MarkUpload() noexcept {
|
bool Buffer::IsRegionUploading(u64 offset, u64 size) const noexcept {
|
||||||
|
return uploads.IsUsed(offset, size, scheduler->GetMasterSemaphore().KnownGpuTick());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::MarkUsage(u64 offset, u64 size) noexcept {
|
||||||
|
tracker.Track(offset, size, scheduler->CurrentTick(),
|
||||||
|
scheduler->GetMasterSemaphore().KnownGpuTick());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::MarkUpload(std::span<const VideoCommon::BufferCopy> copies) noexcept {
|
||||||
last_upload_tick = scheduler->CurrentTick();
|
last_upload_tick = scheduler->CurrentTick();
|
||||||
|
const u64 gpu_tick = scheduler->GetMasterSemaphore().KnownGpuTick();
|
||||||
|
for (const VideoCommon::BufferCopy& copy : copies) {
|
||||||
|
uploads.Track(copy.dst_offset, copy.size, last_upload_tick, gpu_tick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<u8> Buffer::CoherentMapping() noexcept {
|
std::span<u8> Buffer::CoherentMapping() noexcept {
|
||||||
@@ -462,13 +475,8 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
|
|||||||
return static_cast<u32>(device.GetStorageBufferAlignment());
|
return static_cast<u32>(device.GetStorageBufferAlignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
|
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>&) noexcept {
|
||||||
multi_range_buffers.DrainRetired(scheduler);
|
multi_range_buffers.DrainRetired(scheduler);
|
||||||
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
|
||||||
if (scheduler.IsFree(it->LastUsageTick())) {
|
|
||||||
it->ResetUsageTracking();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 BufferCacheRuntime::CurrentTick() {
|
u64 BufferCacheRuntime::CurrentTick() {
|
||||||
@@ -522,8 +530,11 @@ std::span<const u8> BufferCacheRuntime::DirectDownloadSpan(Buffer& buffer) {
|
|||||||
std::span<u8> BufferCacheRuntime::DirectUploadSpan(
|
std::span<u8> BufferCacheRuntime::DirectUploadSpan(
|
||||||
Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies) {
|
Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies) {
|
||||||
const std::span<u8> mapping = buffer.CoherentMapping();
|
const std::span<u8> mapping = buffer.CoherentMapping();
|
||||||
if (mapping.empty() || !scheduler.IsFree(buffer.LastUploadTick()) ||
|
const bool uploading =
|
||||||
!CanReorderUpload(buffer, copies)) {
|
std::ranges::any_of(copies, [&buffer](const VideoCommon::BufferCopy& copy) {
|
||||||
|
return buffer.IsRegionUploading(copy.dst_offset, copy.size);
|
||||||
|
});
|
||||||
|
if (mapping.empty() || uploading || !CanReorderUpload(buffer, copies)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return mapping;
|
return mapping;
|
||||||
|
|||||||
@@ -55,21 +55,13 @@ public:
|
|||||||
return buffer.Location();
|
return buffer.Location();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
|
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept;
|
||||||
return tracker.IsUsed(offset, size);
|
|
||||||
}
|
[[nodiscard]] bool IsRegionUploading(u64 offset, u64 size) const noexcept;
|
||||||
|
|
||||||
void MarkUsage(u64 offset, u64 size) noexcept;
|
void MarkUsage(u64 offset, u64 size) noexcept;
|
||||||
|
|
||||||
void ResetUsageTracking() noexcept {
|
void MarkUpload(std::span<const VideoCommon::BufferCopy> copies) noexcept;
|
||||||
tracker.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 LastUsageTick() const noexcept {
|
|
||||||
return last_usage_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkUpload() noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u64 LastUploadTick() const noexcept {
|
[[nodiscard]] u64 LastUploadTick() const noexcept {
|
||||||
return last_upload_tick;
|
return last_upload_tick;
|
||||||
@@ -94,8 +86,8 @@ private:
|
|||||||
vk::Buffer buffer;
|
vk::Buffer buffer;
|
||||||
std::vector<BufferView> views;
|
std::vector<BufferView> views;
|
||||||
VideoCommon::UsageTracker tracker;
|
VideoCommon::UsageTracker tracker;
|
||||||
|
VideoCommon::UsageTracker uploads;
|
||||||
VkDeviceAddress device_address{};
|
VkDeviceAddress device_address{};
|
||||||
u64 last_usage_tick{};
|
|
||||||
u64 last_upload_tick{};
|
u64 last_upload_tick{};
|
||||||
bool is_null{};
|
bool is_null{};
|
||||||
bool sparse_compatible{};
|
bool sparse_compatible{};
|
||||||
@@ -117,7 +109,7 @@ public:
|
|||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
||||||
DescriptorPool& descriptor_pool);
|
DescriptorPool& descriptor_pool);
|
||||||
|
|
||||||
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
|
void TickFrame(Common::SlotVector<Buffer>&) noexcept;
|
||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
@@ -165,7 +157,7 @@ public:
|
|||||||
void CopyBuffer(Buffer& dst_buffer, VkBuffer src_buffer,
|
void CopyBuffer(Buffer& dst_buffer, VkBuffer src_buffer,
|
||||||
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
|
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
|
||||||
bool can_reorder_upload = false) {
|
bool can_reorder_upload = false) {
|
||||||
dst_buffer.MarkUpload();
|
dst_buffer.MarkUpload(copies);
|
||||||
CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier, can_reorder_upload);
|
CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier, can_reorder_upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ GRenderWindow::~GRenderWindow() {
|
|||||||
input_subsystem->Shutdown();
|
input_subsystem->Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::OnFrameDisplayed() {
|
void GRenderWindow::OnFrameDisplayed([[maybe_unused]] u32 presented_frames) {
|
||||||
input_subsystem->GetTas()->UpdateThread();
|
input_subsystem->GetTas()->UpdateThread();
|
||||||
const InputCommon::TasInput::TasState new_tas_state =
|
const InputCommon::TasInput::TasState new_tas_state =
|
||||||
std::get<0>(input_subsystem->GetTas()->GetStatus());
|
std::get<0>(input_subsystem->GetTas()->GetStatus());
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
~GRenderWindow() override;
|
~GRenderWindow() override;
|
||||||
|
|
||||||
// EmuWindow implementation.
|
// EmuWindow implementation.
|
||||||
void OnFrameDisplayed() override;
|
void OnFrameDisplayed(u32 presented_frames) override;
|
||||||
bool IsShown() const override;
|
bool IsShown() const override;
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user