mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-26 19:11:00 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60633c08af | |||
| 37fe911952 | |||
| f273423b2b | |||
| 87d2f03c39 |
-1
@@ -30,7 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
|
||||
-7
@@ -908,13 +908,6 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
|
||||
-1
@@ -549,7 +549,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -580,8 +580,6 @@
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||
|
||||
@@ -1099,7 +1097,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||
<string name="gpu_fence_behavior_strict">صارم</string>
|
||||
|
||||
<string name="vram_usage_conservative">محافظ</string>
|
||||
<string name="vram_usage_aggressive">عدواني</string>
|
||||
|
||||
@@ -967,7 +967,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||
<string name="gpu_fence_behavior_strict">Strikt</string>
|
||||
|
||||
<string name="vram_usage_conservative">Konservativ</string>
|
||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||
|
||||
@@ -524,8 +524,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||
|
||||
@@ -1038,7 +1036,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||
<string name="gpu_fence_behavior_strict">Estricto</string>
|
||||
|
||||
<string name="vram_usage_conservative">Conservador</string>
|
||||
<string name="vram_usage_aggressive">Agresivo</string>
|
||||
|
||||
@@ -569,8 +569,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||
|
||||
@@ -1088,7 +1086,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||
<string name="gpu_fence_behavior_strict">Строгий</string>
|
||||
|
||||
<string name="vram_usage_conservative">Консервативный</string>
|
||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||
|
||||
@@ -570,8 +570,6 @@
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||
|
||||
@@ -1089,7 +1087,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||
<string name="gpu_fence_behavior_strict">严格</string>
|
||||
|
||||
<string name="vram_usage_conservative">保守式</string>
|
||||
<string name="vram_usage_aggressive">主动式</string>
|
||||
|
||||
@@ -561,8 +561,6 @@
|
||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
||||
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||
|
||||
|
||||
@@ -558,7 +558,6 @@
|
||||
<item>@string/gpu_fence_behavior_immediate</item>
|
||||
<item>@string/gpu_fence_behavior_balanced</item>
|
||||
<item>@string/gpu_fence_behavior_accurate</item>
|
||||
<item>@string/gpu_fence_behavior_strict</item>
|
||||
</string-array>
|
||||
<integer-array name="gpuFenceBehaviorValues">
|
||||
<item>0</item>
|
||||
|
||||
@@ -586,8 +586,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
@@ -1138,7 +1136,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||
<string name="gpu_fence_behavior_strict">Strict</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
|
||||
@@ -178,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||
}
|
||||
|
||||
bool IsGPUFenceBehaviorStrict() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
||||
}
|
||||
|
||||
bool IsFastmemEnabled() {
|
||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||
return bool(values.cpuopt_fastmem);
|
||||
|
||||
@@ -525,7 +525,7 @@ struct Values {
|
||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Strict,
|
||||
GpuFenceBehavior::Accurate,
|
||||
"gpu_fence_behavior",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
@@ -655,13 +655,6 @@ struct Values {
|
||||
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
@@ -979,7 +972,6 @@ bool IsDMALevelSafe();
|
||||
bool IsGPUFenceBehaviorDefault();
|
||||
bool IsGPUFenceBehaviorBalanced();
|
||||
bool IsGPUFenceBehaviorAccurate();
|
||||
bool IsGPUFenceBehaviorStrict();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
|
||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||
ENUM(GpuAccuracy, Low, High);
|
||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
||||
ENUM(CpuBackend, Dynarmic, Nce);
|
||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <algorithm>
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
@@ -20,32 +21,36 @@ namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
static std::mutex vector_regions_mutex {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +82,16 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State == MEM_COMMIT) {
|
||||
DWORD old_protect {};
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -123,7 +136,8 @@ void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
@@ -149,6 +163,8 @@ void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
|
||||
@@ -28,9 +28,9 @@ constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
@@ -81,8 +81,8 @@ public:
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
||||
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
@@ -103,9 +103,8 @@ public:
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
if (IsCommittedPage(index) || CommitPage(index))
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
@@ -177,16 +176,22 @@ private:
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
constexpr bool CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
if (!CommitVectorPage(page, true)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr void DecommitPage(std::size_t index) noexcept {
|
||||
|
||||
@@ -201,7 +201,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||
|
||||
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
|
||||
ir_block.Reset(descriptor);
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
}
|
||||
|
||||
|
||||
@@ -366,8 +366,7 @@ A64AddressSpace::A64AddressSpace(const A64::UserConfig& conf)
|
||||
|
||||
void A64AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
|
||||
ir_block.Reset(descriptor);
|
||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{descriptor}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{descriptor}, conf);
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||
}
|
||||
|
||||
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
|
||||
Optimization::Optimize(ir_block, conf, {.sha256 = true});
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||
}
|
||||
|
||||
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ private:
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A32::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A32::Translate(ir_block, arch_descriptor, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
A32::Translate(ir_block, arch_descriptor, conf);
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block);
|
||||
}
|
||||
|
||||
@@ -254,11 +254,10 @@ private:
|
||||
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
||||
|
||||
// JIT Compile
|
||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A64::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A64::Translate(ir_block, arch_descriptor, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
A64::Translate(ir_block, arch_descriptor, conf);
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block).entrypoint;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,16 @@
|
||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
|
||||
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
|
||||
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
|
||||
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
|
||||
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
|
||||
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(block, descriptor, tcb, options);
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
|
||||
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(block, descriptor, conf);
|
||||
}
|
||||
|
||||
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/interface/A32/arch_version.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
class Block;
|
||||
@@ -20,28 +21,14 @@ namespace Dynarmic::A32 {
|
||||
class LocationDescriptor;
|
||||
struct TranslateCallbacks;
|
||||
|
||||
struct TranslationOptions {
|
||||
ArchVersion arch_version;
|
||||
|
||||
/// This changes what IR we emit when we translate an unpredictable instruction.
|
||||
/// If this is false, the ExceptionRaised IR instruction is emitted.
|
||||
/// If this is true, we define some behaviour for some instructions.
|
||||
bool define_unpredictable_behaviour = false;
|
||||
|
||||
/// This changes what IR we emit when we translate a hint instruction.
|
||||
/// If this is false, we treat the instruction as a NOP.
|
||||
/// If this is true, we emit an ExceptionRaised instruction.
|
||||
bool hook_hint_instructions = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function translates instructions in memory into our intermediate representation.
|
||||
* @param descriptor The starting location of the basic block. Includes information like PC, Thumb state, &c.
|
||||
* @param tcb The callbacks we should use to read emulated memory.
|
||||
* @param options Configures how certain instructions are translated.
|
||||
* @param conf Configures how certain instructions are translated.
|
||||
* @return A translated basic block in the intermediate representation.
|
||||
*/
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
|
||||
|
||||
/**
|
||||
* This function translates a single provided instruction into our intermediate representation.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -10,7 +13,7 @@ namespace Dynarmic::A32 {
|
||||
|
||||
// BKPT #<imm16>
|
||||
bool TranslatorVisitor::arm_BKPT(Cond cond, Imm<12> /*imm12*/, Imm<4> /*imm4*/) {
|
||||
if (cond != Cond::AL && !options.define_unpredictable_behaviour) {
|
||||
if (cond != Cond::AL && !conf.define_unpredictable_behaviour) {
|
||||
return UnpredictableInstruction();
|
||||
}
|
||||
// UNPREDICTABLE: The instruction executes conditionally.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
|
||||
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
|
||||
@@ -25,12 +26,14 @@ enum class Exception;
|
||||
struct TranslatorVisitor final {
|
||||
using instruction_return_type = bool;
|
||||
|
||||
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options)
|
||||
: ir(block, descriptor, options.arch_version), options(options) {}
|
||||
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf)
|
||||
: ir(block, descriptor, conf.arch_version)
|
||||
, conf(conf)
|
||||
{}
|
||||
|
||||
A32::IREmitter ir;
|
||||
ConditionalState cond_state = ConditionalState::None;
|
||||
TranslationOptions options;
|
||||
A32::UserConfig const& conf;
|
||||
|
||||
size_t current_instruction_size;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2019 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -9,7 +12,7 @@
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
bool TranslatorVisitor::arm_PLD_imm(bool /*add*/, bool R, Reg /*n*/, Imm<12> /*imm12*/) {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +21,7 @@ bool TranslatorVisitor::arm_PLD_imm(bool /*add*/, bool R, Reg /*n*/, Imm<12> /*i
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_PLD_reg(bool /*add*/, bool R, Reg /*n*/, Imm<5> /*imm5*/, ShiftType /*shift*/, Reg /*m*/) {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,7 +30,7 @@ bool TranslatorVisitor::arm_PLD_reg(bool /*add*/, bool R, Reg /*n*/, Imm<5> /*im
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_SEV() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,7 +38,7 @@ bool TranslatorVisitor::arm_SEV() {
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_SEVL() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -43,7 +46,7 @@ bool TranslatorVisitor::arm_SEVL() {
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_WFE() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,7 +54,7 @@ bool TranslatorVisitor::arm_WFE() {
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_WFI() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,7 +62,7 @@ bool TranslatorVisitor::arm_WFI() {
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::arm_YIELD() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -642,7 +642,7 @@ bool TranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
|
||||
|
||||
// SEV<c>
|
||||
bool TranslatorVisitor::thumb16_SEV() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::SendEvent);
|
||||
@@ -650,7 +650,7 @@ bool TranslatorVisitor::thumb16_SEV() {
|
||||
|
||||
// SEVL<c>
|
||||
bool TranslatorVisitor::thumb16_SEVL() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::SendEventLocal);
|
||||
@@ -658,7 +658,7 @@ bool TranslatorVisitor::thumb16_SEVL() {
|
||||
|
||||
// WFE<c>
|
||||
bool TranslatorVisitor::thumb16_WFE() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::WaitForEvent);
|
||||
@@ -666,7 +666,7 @@ bool TranslatorVisitor::thumb16_WFE() {
|
||||
|
||||
// WFI<c>
|
||||
bool TranslatorVisitor::thumb16_WFI() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::WaitForInterrupt);
|
||||
@@ -674,7 +674,7 @@ bool TranslatorVisitor::thumb16_WFI() {
|
||||
|
||||
// YIELD<c>
|
||||
bool TranslatorVisitor::thumb16_YIELD() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::Yield);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2021 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -8,7 +11,7 @@
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
static bool PLDHandler(TranslatorVisitor& v, bool W) {
|
||||
if (!v.options.hook_hint_instructions) {
|
||||
if (!v.conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +21,7 @@ static bool PLDHandler(TranslatorVisitor& v, bool W) {
|
||||
}
|
||||
|
||||
static bool PLIHandler(TranslatorVisitor& v) {
|
||||
if (!v.options.hook_hint_instructions) {
|
||||
if (!v.conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
|
||||
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
|
||||
const bool single_step = descriptor.SingleStepping();
|
||||
TranslatorVisitor visitor{block, descriptor, options};
|
||||
TranslatorVisitor visitor{block, descriptor, conf};
|
||||
bool should_continue = true;
|
||||
|
||||
auto tcb = conf.callbacks;
|
||||
do {
|
||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||
u64 ticks_for_instruction = 1;
|
||||
|
||||
@@ -103,10 +103,12 @@ inline bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) noexcept {
|
||||
|
||||
} // namespace
|
||||
|
||||
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
|
||||
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
|
||||
const bool single_step = descriptor.SingleStepping();
|
||||
TranslatorVisitor visitor{block, descriptor, options};
|
||||
TranslatorVisitor visitor{block, descriptor, conf};
|
||||
bool should_continue = true;
|
||||
|
||||
auto tcb = conf.callbacks;
|
||||
do {
|
||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||
u64 ticks_for_instruction = 1;
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
#include "dynarmic/frontend/A64/decoder/a64.h"
|
||||
#include "dynarmic/frontend/A64/translate/impl/impl.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
|
||||
namespace Dynarmic::A64 {
|
||||
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) {
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf) {
|
||||
const bool single_step = descriptor.SingleStepping();
|
||||
TranslatorVisitor visitor{block, descriptor, std::move(options)};
|
||||
TranslatorVisitor visitor{block, descriptor, conf};
|
||||
|
||||
bool should_continue = true;
|
||||
do {
|
||||
const u64 pc = visitor.ir.current_location->PC();
|
||||
if (const auto instruction = memory_read_code(pc)) {
|
||||
if (const auto instruction = conf.callbacks->MemoryReadCode(pc)) {
|
||||
if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) {
|
||||
should_continue = *decoder;
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
@@ -22,32 +23,14 @@ namespace A64 {
|
||||
|
||||
class LocationDescriptor;
|
||||
|
||||
using MemoryReadCodeFuncType = std::function<std::optional<u32>(u64 vaddr)>;
|
||||
|
||||
struct TranslationOptions {
|
||||
/// This changes what IR we emit when we translate an unpredictable instruction.
|
||||
/// If this is false, the ExceptionRaised IR instruction is emitted.
|
||||
/// If this is true, we define some behaviour for some instructions.
|
||||
bool define_unpredictable_behaviour = false;
|
||||
|
||||
/// This tells the translator a wall clock will be used, thus allowing it
|
||||
/// to avoid writting certain unnecessary code only needed for cycle timers.
|
||||
bool wall_clock_cntpct = false;
|
||||
|
||||
/// This changes what IR we emit when we translate a hint instruction.
|
||||
/// If this is false, we treat the instruction as a NOP.
|
||||
/// If this is true, we emit an ExceptionRaised instruction.
|
||||
bool hook_hint_instructions = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function translates instructions in memory into our intermediate representation.
|
||||
* @param descriptor The starting location of the basic block. Includes information like PC, FPCR state, &c.
|
||||
* @param memory_read_code The function we should use to read emulated memory.
|
||||
* @param options Configures how certain instructions are translated.
|
||||
* @param conf Configures how certain instructions are translated.
|
||||
* @return A translated basic block in the intermediate representation.
|
||||
*/
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options);
|
||||
void Translate(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf);
|
||||
|
||||
/**
|
||||
* This function translates a single provided instruction into our intermediate representation.
|
||||
|
||||
@@ -15,17 +15,20 @@
|
||||
#include "dynarmic/frontend/A64/a64_types.h"
|
||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||
#include "dynarmic/frontend/imm.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Dynarmic::A64 {
|
||||
|
||||
struct TranslatorVisitor final {
|
||||
using instruction_return_type = bool;
|
||||
|
||||
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, TranslationOptions options)
|
||||
: ir(block, descriptor), options(std::move(options)) {}
|
||||
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf)
|
||||
: ir(block, descriptor)
|
||||
, conf(conf)
|
||||
{}
|
||||
|
||||
A64::IREmitter ir;
|
||||
TranslationOptions options;
|
||||
A64::UserConfig const& conf;
|
||||
|
||||
bool UnpredictableInstruction();
|
||||
bool DecodeError();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -25,7 +28,7 @@ static bool ExclusiveSharedDecodeAndOperation(TranslatorVisitor& v, bool pair, s
|
||||
if (memop == IR::MemOp::LOAD && pair && Rt == *Rt2) {
|
||||
return v.UnpredictableInstruction();
|
||||
} else if (memop == IR::MemOp::STORE && (*Rs == Rt || (pair && *Rs == *Rt2))) {
|
||||
if (!v.options.define_unpredictable_behaviour) {
|
||||
if (!v.conf.define_unpredictable_behaviour) {
|
||||
return v.UnpredictableInstruction();
|
||||
}
|
||||
// UNPREDICTABLE: The Constraint_NONE case is executed.
|
||||
|
||||
@@ -42,35 +42,35 @@ bool TranslatorVisitor::NOP() {
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::YIELD() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::Yield);
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::WFE() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::WaitForEvent);
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::WFI() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::WaitForInterrupt);
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::SEV() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::SendEvent);
|
||||
}
|
||||
|
||||
bool TranslatorVisitor::SEVL() {
|
||||
if (!options.hook_hint_instructions) {
|
||||
if (!conf.hook_hint_instructions) {
|
||||
return true;
|
||||
}
|
||||
return RaiseException(Exception::SendEventLocal);
|
||||
@@ -129,7 +129,7 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3
|
||||
return true;
|
||||
case SystemRegisterEncoding::CNTPCT_EL0:
|
||||
// HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date.
|
||||
if (!ir.block.instructions.empty() && !options.wall_clock_cntpct) {
|
||||
if (!ir.block.instructions.empty() && !conf.wall_clock_cntpct) {
|
||||
ir.block.CycleCount()--;
|
||||
ir.SetTerm(IR::Term::LinkBlock{*ir.current_location});
|
||||
return false;
|
||||
|
||||
@@ -180,7 +180,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32::UserConf
|
||||
while (num_insts < instructions_to_execute_count) {
|
||||
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
|
||||
ir_block.Reset(descriptor);
|
||||
A32::Translate(ir_block, descriptor, &test_env, {});
|
||||
A32::Translate(ir_block, descriptor, config);
|
||||
Optimization::Optimize(ir_block, config, {});
|
||||
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
|
||||
printf("\n\nx86_64:\n");
|
||||
|
||||
@@ -268,7 +268,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
|
||||
// const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
|
||||
// const A64::LocationDescriptor location{instructions_start, FP::FPCR{fpcr}};
|
||||
// IR::Block ir_block{location};
|
||||
// A64::Translate(ir_block, location, get_code, {});
|
||||
// A64::Translate(ir_block, location, config);
|
||||
// fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
|
||||
// Optimization::Optimize(ir_block, conf, {});
|
||||
// fmt::print("Optimized IR:\n{}\n", IR::DumpBlock(ir_block));
|
||||
|
||||
@@ -761,9 +761,6 @@ void EmulatedController::StartMotionCalibration() {
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
if (index >= controller.button_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -916,21 +913,10 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
if (npad_type == NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Handheld) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
}
|
||||
} else if (npad_type != NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Player1) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
} else if (player.connected && !controller_connected[player_index]) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
}
|
||||
}
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
if (player.connected) {
|
||||
Connect();
|
||||
}
|
||||
|
||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "hid_core/frontend/motion_input.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "hid_core/irsensor/irs_types.h"
|
||||
|
||||
@@ -585,7 +584,6 @@ private:
|
||||
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
||||
DEFAULT_VIBRATION_VALUE};
|
||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
||||
|
||||
// Atomically synched values
|
||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||
|
||||
@@ -226,9 +226,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting."));
|
||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||
tr("May reduce shader stutter."));
|
||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||
@@ -442,7 +440,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
||||
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
||||
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
||||
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
||||
|
||||
@@ -249,6 +249,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||
if (has_new_downloads) {
|
||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||
if (should_sync) {
|
||||
runtime.Finish();
|
||||
}
|
||||
}
|
||||
|
||||
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||
@@ -1230,7 +1234,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
||||
buffer.MarkUsage(offset, size);
|
||||
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true);
|
||||
}
|
||||
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
@@ -1516,10 +1520,12 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
if (needs_sync) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
}
|
||||
}
|
||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||
gpu_modified_ranges.Add(device_addr, size);
|
||||
@@ -1535,8 +1541,11 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
||||
const BufferId buffer_id = page_table[page];
|
||||
if (buffer_id) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
WaitForGpuFenceIfNeeded(buffer);
|
||||
if (buffer.IsInBounds(device_addr, size)) {
|
||||
const bool should_sync = Settings::IsGPUFenceBehaviorAccurate();
|
||||
if (should_sync) {
|
||||
SynchronizeBufferWrites(buffer);
|
||||
}
|
||||
bool usable = true;
|
||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
||||
@@ -1552,17 +1561,11 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
||||
void BufferCache<P>::SynchronizeBufferWrites(Buffer& buffer) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||
if (gpu_fence_accurate || gpu_fence_strict) {
|
||||
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
||||
const u64 buffer_tick = buffer.getWriteTick();
|
||||
const u64 gpu_tick = runtime.KnownGpuTick();
|
||||
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
||||
runtime.Wait(buffer_tick);
|
||||
}
|
||||
const u64 buffer_tick = buffer.getWriteTick();
|
||||
if (!runtime.IsFree(buffer_tick)) {
|
||||
runtime.Wait(buffer_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1795,9 +1798,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
if (immediate_buffer.empty()) {
|
||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||
}
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||
}
|
||||
@@ -1816,9 +1816,6 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
for (BufferCopy& copy : copies) {
|
||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||
// Apply the staging offset
|
||||
copy.src_offset += upload_staging.offset;
|
||||
|
||||
@@ -430,11 +430,11 @@ private:
|
||||
|
||||
void UpdateComputeTextureBuffers();
|
||||
|
||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false);
|
||||
|
||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
||||
|
||||
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||
void SynchronizeBufferWrites(Buffer& buffer);
|
||||
|
||||
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
void SignalFence(std::function<void()>&& func) {
|
||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict();
|
||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool should_flush = ShouldFlush();
|
||||
if constexpr (!can_async_check) {
|
||||
TryReleasePendingFences<false>();
|
||||
|
||||
@@ -260,7 +260,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
||||
};
|
||||
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
||||
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence;
|
||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence;
|
||||
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
||||
pointer, pointer_timestamp] {
|
||||
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
||||
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
if (host_visible) {
|
||||
return StagingBufferRef{};
|
||||
}
|
||||
return staging_pool.Request(device, size_bytes, MemoryUsage::Upload);
|
||||
return staging_pool.Request(size_bytes, MemoryUsage::Upload);
|
||||
}();
|
||||
|
||||
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
|
||||
@@ -375,11 +375,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
||||
}
|
||||
|
||||
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
||||
return staging_pool.Request(device, size, MemoryUsage::Upload);
|
||||
return staging_pool.Request(size, MemoryUsage::Upload);
|
||||
}
|
||||
|
||||
StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
||||
return staging_pool.Request(device, size, MemoryUsage::Download, deferred);
|
||||
return staging_pool.Request(size, MemoryUsage::Download, deferred);
|
||||
}
|
||||
|
||||
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
|
||||
@@ -419,15 +419,15 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
|
||||
}
|
||||
|
||||
u64 BufferCacheRuntime::CurrentTick() {
|
||||
return scheduler.GetMasterSemaphore().CurrentTick();
|
||||
return scheduler.CurrentTick();
|
||||
}
|
||||
|
||||
u64 BufferCacheRuntime::KnownGpuTick() {
|
||||
return scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||
bool BufferCacheRuntime::IsFree(u64 tick) {
|
||||
return scheduler.IsFree(tick);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::Wait(u64 buffer_tick) {
|
||||
scheduler.Wait(buffer_tick);
|
||||
void BufferCacheRuntime::Wait(u64 tick) {
|
||||
scheduler.Wait(tick);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::Finish() {
|
||||
|
||||
@@ -112,9 +112,9 @@ public:
|
||||
|
||||
u64 CurrentTick();
|
||||
|
||||
u64 KnownGpuTick();
|
||||
bool IsFree(u64 tick);
|
||||
|
||||
void Wait(u64 buffer_tick);
|
||||
void Wait(u64 tick);
|
||||
|
||||
void Finish();
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
|
||||
[[maybe_unused]] u32 binding_index,
|
||||
u32 size) {
|
||||
const StagingBufferRef ref = staging_pool.Request(device, size, MemoryUsage::Upload);
|
||||
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
|
||||
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
|
||||
static_cast<u32>(ref.offset), size);
|
||||
return ref.mapped_span;
|
||||
|
||||
@@ -287,7 +287,7 @@ Uint8Pass::~Uint8Pass() = default;
|
||||
std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
|
||||
u32 src_offset) {
|
||||
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
|
||||
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
|
||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
||||
@@ -345,7 +345,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
|
||||
const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
|
||||
|
||||
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
|
||||
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
|
||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
||||
|
||||
@@ -852,7 +852,7 @@ public:
|
||||
|
||||
void PushUnsyncedQueries() override {
|
||||
CloseCounter();
|
||||
auto staging_ref = staging_pool.Request(device,
|
||||
auto staging_ref = staging_pool.Request(
|
||||
pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true);
|
||||
size_t offset_base = staging_ref.offset;
|
||||
for (auto q : pending_flush_queries) {
|
||||
@@ -1657,7 +1657,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
|
||||
impl->copies_setup.clear();
|
||||
impl->copies_setup.resize(impl->little_cache.size());
|
||||
if constexpr (SyncValuesType::GeneratesBaseBuffer) {
|
||||
ref = impl->staging_pool.Request(impl->device, total_size, MemoryUsage::Upload);
|
||||
ref = impl->staging_pool.Request(total_size, MemoryUsage::Upload);
|
||||
size_t current_offset = ref.offset;
|
||||
size_t accumulated_size = 0;
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
|
||||
@@ -28,42 +28,55 @@ using namespace Common::Literals;
|
||||
// Maximum potential alignment of a Vulkan buffer
|
||||
constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
||||
|
||||
size_t GetStreamBufferSize(const Device& device, size_t max_stream_buffer_size, size_t max_alignment) {
|
||||
// Stream buffer size in bytes
|
||||
// *NIX drivers are more sensitive to increased buffers for streaming.
|
||||
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
||||
// - GTX 960 on Windows will not OOM with 256mib
|
||||
// - GT 1030 on ^NIX will OOM with 256mib
|
||||
#if defined(__FreeBSD__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||
#else
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
#endif
|
||||
|
||||
size_t GetStreamBufferSize(const Device& device) {
|
||||
if (!device.HasDebuggingToolAttached()) {
|
||||
return max_stream_buffer_size;
|
||||
return MAX_STREAM_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
VkDeviceSize size{0};
|
||||
bool has_device_local_host_visible_heap{};
|
||||
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](size_t index, VkMemoryHeap& heap) {
|
||||
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](
|
||||
size_t index, VkMemoryHeap& heap) {
|
||||
has_device_local_host_visible_heap = true;
|
||||
size = std::max<size_t>(size, heap.size);
|
||||
size = (std::max)(size, heap.size);
|
||||
});
|
||||
if (has_device_local_host_visible_heap) {
|
||||
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
|
||||
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
|
||||
// as the heap will be much larger.
|
||||
if (size <= max_stream_buffer_size) {
|
||||
if (size <= MAX_STREAM_BUFFER_SIZE) {
|
||||
size = size * 40 / 100;
|
||||
}
|
||||
} else {
|
||||
size = max_stream_buffer_size;
|
||||
size = MAX_STREAM_BUFFER_SIZE;
|
||||
}
|
||||
return std::min<size_t>(Common::AlignUp(size, max_alignment), max_stream_buffer_size);
|
||||
return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
|
||||
: memory_allocator{memory_allocator_}, scheduler{scheduler_}
|
||||
, stream_buffer_size{GetStreamBufferSize(device, 256_MiB, MAX_ALIGNMENT)}
|
||||
{
|
||||
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_)
|
||||
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
||||
stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size /
|
||||
StagingBufferPool::NUM_SYNCS} {
|
||||
VkBufferCreateInfo stream_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = stream_buffer_size,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
@@ -74,16 +87,7 @@ StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memo
|
||||
if (device.IsBufferDeviceAddressSupported()) {
|
||||
stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||
}
|
||||
// Some drivers are more sensitive to increased buffer sizes
|
||||
try {
|
||||
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
||||
} catch (vk::Exception& e) {
|
||||
LOG_ERROR(Render_Vulkan, "Can't fit {} bytes buffer, halving", stream_ci.size);
|
||||
stream_buffer_size = GetStreamBufferSize(device, 128_MiB, MAX_ALIGNMENT);
|
||||
stream_ci.size = stream_buffer_size;
|
||||
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
||||
}
|
||||
region_size = stream_buffer_size / StagingBufferPool::NUM_SYNCS;
|
||||
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
stream_buffer.SetObjectNameEXT("Stream Buffer");
|
||||
}
|
||||
@@ -96,10 +100,11 @@ StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memo
|
||||
|
||||
StagingBufferPool::~StagingBufferPool() = default;
|
||||
|
||||
StagingBufferRef StagingBufferPool::Request(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||
return (!deferred && usage == MemoryUsage::Upload && size <= region_size)
|
||||
? GetStreamBuffer(device, size)
|
||||
: GetStagingBuffer(device, size, usage, deferred);
|
||||
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
|
||||
if (!deferred && usage == MemoryUsage::Upload && size <= region_size) {
|
||||
return GetStreamBuffer(size);
|
||||
}
|
||||
return GetStagingBuffer(size, usage, deferred);
|
||||
}
|
||||
|
||||
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
|
||||
@@ -122,10 +127,11 @@ void StagingBufferPool::TickFrame() {
|
||||
ReleaseCache(MemoryUsage::Download);
|
||||
}
|
||||
|
||||
StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t size) {
|
||||
if (AreRegionsActive(Region(free_iterator) + 1, (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
|
||||
StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
||||
if (AreRegionsActive(Region(free_iterator) + 1,
|
||||
(std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
|
||||
// Avoid waiting for the previous usages to be free
|
||||
return GetStagingBuffer(device, size, MemoryUsage::Upload);
|
||||
return GetStagingBuffer(size, MemoryUsage::Upload);
|
||||
}
|
||||
const u64 current_tick = scheduler.CurrentTick();
|
||||
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator),
|
||||
@@ -134,14 +140,15 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
|
||||
free_iterator = (std::max)(free_iterator, iterator + size);
|
||||
|
||||
if (iterator + size >= stream_buffer_size) {
|
||||
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick);
|
||||
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS,
|
||||
current_tick);
|
||||
used_iterator = 0;
|
||||
iterator = 0;
|
||||
free_iterator = size;
|
||||
|
||||
if (AreRegionsActive(0, Region(size) + 1)) {
|
||||
// Avoid waiting for the previous usages to be free
|
||||
return GetStagingBuffer(device, size, MemoryUsage::Upload);
|
||||
return GetStagingBuffer(size, MemoryUsage::Upload);
|
||||
}
|
||||
}
|
||||
const size_t offset = iterator;
|
||||
@@ -149,7 +156,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
|
||||
return StagingBufferRef{
|
||||
.buffer = *stream_buffer,
|
||||
.device_address = stream_buffer_address,
|
||||
.offset = VkDeviceSize(offset),
|
||||
.offset = static_cast<VkDeviceSize>(offset),
|
||||
.mapped_span = stream_pointer.subspan(offset, size),
|
||||
.usage{},
|
||||
.log2_level{},
|
||||
@@ -159,18 +166,21 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
|
||||
|
||||
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
|
||||
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, [gpu_tick](u64 sync_tick) {
|
||||
return gpu_tick < sync_tick;
|
||||
});
|
||||
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end,
|
||||
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
|
||||
};
|
||||
|
||||
StagingBufferRef StagingBufferPool::GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred))
|
||||
StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
|
||||
bool deferred) {
|
||||
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
|
||||
return *ref;
|
||||
return CreateStagingBuffer(device, size, usage, deferred);
|
||||
}
|
||||
return CreateStagingBuffer(size, usage, deferred);
|
||||
}
|
||||
|
||||
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred) {
|
||||
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
|
||||
MemoryUsage usage,
|
||||
bool deferred) {
|
||||
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)];
|
||||
|
||||
const auto is_free = [this](const StagingBuffer& entry) {
|
||||
@@ -192,7 +202,7 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
|
||||
return it->Ref();
|
||||
}
|
||||
|
||||
StagingBufferRef StagingBufferPool::CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred) {
|
||||
auto const log2_size = Common::Log2Ceil<u32>(u32(size));
|
||||
VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
|
||||
@@ -33,10 +33,11 @@ class StagingBufferPool {
|
||||
public:
|
||||
static constexpr size_t NUM_SYNCS = 16;
|
||||
|
||||
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler);
|
||||
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
|
||||
Scheduler& scheduler);
|
||||
~StagingBufferPool();
|
||||
|
||||
StagingBufferRef Request(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
|
||||
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
|
||||
void FreeDeferred(StagingBufferRef& ref);
|
||||
|
||||
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
|
||||
@@ -83,18 +84,27 @@ private:
|
||||
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
|
||||
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
|
||||
|
||||
StagingBufferRef GetStreamBuffer(const Device& device, size_t size);
|
||||
StagingBufferRef GetStreamBuffer(size_t size);
|
||||
|
||||
bool AreRegionsActive(size_t region_begin, size_t region_end) const;
|
||||
StagingBufferRef GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
|
||||
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred);
|
||||
StagingBufferRef CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred);
|
||||
|
||||
StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
|
||||
|
||||
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
|
||||
bool deferred);
|
||||
|
||||
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
|
||||
|
||||
StagingBuffersCache& GetCache(MemoryUsage usage);
|
||||
|
||||
void ReleaseCache(MemoryUsage usage);
|
||||
|
||||
void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
|
||||
size_t Region(size_t iter) const noexcept {
|
||||
return iter / region_size;
|
||||
}
|
||||
|
||||
const Device& device;
|
||||
MemoryAllocator& memory_allocator;
|
||||
Scheduler& scheduler;
|
||||
|
||||
|
||||
@@ -975,11 +975,11 @@ void TextureCacheRuntime::Finish() {
|
||||
}
|
||||
|
||||
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
|
||||
return staging_buffer_pool.Request(device, size, MemoryUsage::Upload, deferred);
|
||||
return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred);
|
||||
}
|
||||
|
||||
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
||||
return staging_buffer_pool.Request(device, size, MemoryUsage::Download, deferred);
|
||||
return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred);
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
|
||||
|
||||
Reference in New Issue
Block a user