Compare commits

..

10 Commits

Author SHA1 Message Date
crueter 03ac968ca7 Update README.md 2026-07-25 04:45:46 +02:00
crueter f5d1a78846 Move PULL_REQUEST_TEMPLATE to a single file
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-07-24 22:45:03 -04:00
crueter 5d3ad94e18 [meta] Add a pull request template for GitHub (#4226)
Requires contributors to GitHub to read the policy.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4226
2026-07-25 04:40:57 +02:00
crueter 479df9809a [filesystem] Return ResultPathAlreadyExists from CreateDirectory if path exists (#4224)
Returning success when the path already exists confuses games like Eastward which use this result in their save logic.

Closes https://github.com/eden-emulator/mirror/pull/10

Signed-off-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Tom Pratt <tom.pratt@outlook.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4224
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-25 01:48:49 +02:00
Maufeat 5b4c29b123 [file_sys] Quick sandbox escape fix (#4223)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4223
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-25 00:31:22 +02:00
lizzie 5b4dc32c1a [core/hle/service/am] Stub ISelfController::IsIlluminanceAvailable (#4163)
MelonDS calls this and otherwise seems innocuous to implement.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4163
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-24 22:23:09 +02:00
Maufeat a90ba7f6ac [hle] stub SetMouseLibraryVersion (#4222)
fix for Avatar Legends

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4222
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-24 18:38:30 +02:00
PavelBARABANOV 9b2a36791d [android] remove GPU accuracy balanced (#4220)
Support for it was removed in commit https://git.eden-emu.dev/eden-emu/eden/commit/a27d35463ef9d78553313b568e96f98097be7c11

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4220
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-23 22:26:47 +02:00
xbzk b85f289048 [nce, fs] tico support, fix FS bug that would nuke entire 'switch' folder (#4086)
Contains the minimal set of functionalities to allow tico installer succeed, and tico work normally EXCEPT for game launching (which me or someone else will investigate later)

First three commits are from PR 4012.

The other six, kinda dizzy to explain each one. All were implemented based on tico source, switchbrew and libnx.
Hopefully the commit messages will do.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4086
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-23 21:59:55 +02:00
lizzie 89004124a5 [video_core] use bool params for read/writes and cascade them thru the calltree (#4001)
should make codegen a tad bit better and reduce icache pressure for what is otherwise a glorified memcpy

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4001
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-18 21:01:58 +02:00
136 changed files with 1802 additions and 5826 deletions
+5
View File
@@ -0,0 +1,5 @@
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
-------------------
+2
View File
@@ -2,6 +2,8 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
-->
+1 -1
View File
@@ -65,7 +65,7 @@ android {
defaultConfig {
applicationId = "dev.eden.eden_emulator"
minSdk = 33
minSdk = 24
targetSdk = 36
versionName = getGitVersion()
versionCode = autoVersion
@@ -27,7 +27,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
@@ -37,8 +36,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_DEBUG("debug"),
RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"),
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
RENDERER_DYNAMIC_RENDERING("dynamic_rendering"),
RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT("workgroup_memory_explicit_layout"),
RENDERER_SAMPLE_SHADING("sample_shading"),
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
PICTURE_IN_PICTURE("picture_in_picture"),
@@ -155,20 +155,6 @@ abstract class SettingsItem(
descriptionId = R.string.vertex_input_dynamic_state_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DYNAMIC_RENDERING,
titleId = R.string.dynamic_rendering,
descriptionId = R.string.dynamic_rendering_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT,
titleId = R.string.workgroup_memory_explicit_layout,
descriptionId = R.string.workgroup_memory_explicit_layout_description
)
)
put(
SliderSetting(
IntSetting.RENDERER_SAMPLE_SHADING,
@@ -608,7 +594,7 @@ abstract class SettingsItem(
IntSetting.ANDROID_PIPELINE_WORKERS,
titleId = R.string.pipeline_worker_cores,
descriptionId = R.string.pipeline_worker_cores_description,
min = 2,
min = 4,
max = 8,
units = "cores"
)
@@ -699,13 +685,6 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_shaders_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_UNIFIED_MEMORY,
titleId = R.string.renderer_unified_memory,
descriptionId = R.string.renderer_unified_memory_description
)
)
put(
SingleChoiceSetting(
IntSetting.FAST_GPU_TIME,
@@ -304,7 +304,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RESCALE_HACK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
@@ -314,8 +313,6 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_DYNA_STATE.key)
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
add(BooleanSetting.RENDERER_DYNAMIC_RENDERING.key)
add(BooleanSetting.RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT.key)
add(IntSetting.RENDERER_SAMPLE_SHADING.key)
add(HeaderSetting(R.string.display))
@@ -147,6 +147,13 @@ namespace AndroidSettings {
&show_performance_overlay};
Settings::Setting<s32> pipeline_worker_count{linkage, 4, "pipeline_worker_count",
Settings::Category::Android,
Settings::Specialization::Default,
true,
true};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> overlay_snap_to_grid{linkage, false, "overlay_snap_to_grid",
@@ -1000,7 +1000,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">سريع</string>
<string name="renderer_accuracy_medium">متوازن</string>
<string name="renderer_accuracy_high">دقيق</string>
<!-- DMA Accuracy -->
@@ -707,7 +707,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rychlý</string>
<string name="renderer_accuracy_medium">Vyvážený</string>
<string name="renderer_accuracy_high">Přesný</string>
<!-- DMA Accuracy -->
@@ -927,7 +927,6 @@ Wirklich fortfahren?</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Schnell</string>
<string name="renderer_accuracy_medium">Ausgeglichen</string>
<string name="renderer_accuracy_high">Genau</string>
<!-- DMA Accuracy -->
@@ -993,7 +993,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rápido</string>
<string name="renderer_accuracy_medium">Equilibrado</string>
<string name="renderer_accuracy_high">Preciso</string>
<!-- DMA Accuracy -->
@@ -939,7 +939,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rapide</string>
<string name="renderer_accuracy_medium">Moyen</string>
<string name="renderer_accuracy_high">Précis</string>
<!-- DMA Accuracy -->
@@ -893,7 +893,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Szybkie</string>
<string name="renderer_accuracy_medium">Zrównoważony</string>
<string name="renderer_accuracy_high">Dokładny</string>
<!-- DMA Accuracy -->
@@ -840,7 +840,6 @@
<string name="renderer_none">Nenhum</string>
<string name="renderer_accuracy_medium">Média</string>
<string name="renderer_accuracy_high">Alta</string>
<!-- DMA Accuracy -->
@@ -983,7 +983,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Быстрый</string>
<string name="renderer_accuracy_medium">Сбалансированный</string>
<string name="renderer_accuracy_high">Точный</string>
<!-- DMA Accuracy -->
@@ -986,7 +986,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Швидко</string>
<string name="renderer_accuracy_medium">Збалансовано</string>
<string name="renderer_accuracy_high">Точно</string>
<!-- DMA Accuracy -->
@@ -990,7 +990,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">快速</string>
<string name="renderer_accuracy_medium">均衡</string>
<string name="renderer_accuracy_high">精确</string>
<!-- DMA Accuracy -->
@@ -843,7 +843,6 @@
<string name="renderer_none">無</string>
<string name="renderer_accuracy_medium">平衡</string>
<string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy -->
@@ -103,14 +103,12 @@
<string-array name="rendererAccuracyNames">
<item>@string/renderer_accuracy_low</item>
<item>@string/renderer_accuracy_medium</item>
<item>@string/renderer_accuracy_high</item>
</string-array>
<integer-array name="rendererAccuracyValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<!-- VRAM USAGE MODE CHOICES -->
@@ -479,7 +479,7 @@
<string name="advanced">Advanced</string>
<string name="renderer_accuracy">GPU Mode</string>
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
<string name="dma_accuracy">DMA Accuracy</string>
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
<string name="gpu_fence_behavior">GPU Fence Behavior</string>
@@ -524,8 +524,6 @@
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="renderer_unified_memory">Unified memory access (UMA)</string>
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
@@ -546,10 +544,6 @@
<string name="disabled">Disabled</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Enabling this feature allows for more flexible vertex input handling, potentially reducing pipeline compilation time in vertex/buffer.</string>
<string name="dynamic_rendering">Dynamic Rendering</string>
<string name="dynamic_rendering_description">Render without render pass and framebuffer objects. Results vary by driver: some gain performance, others lose it.</string>
<string name="workgroup_memory_explicit_layout">Workgroup Memory Explicit Layout</string>
<string name="workgroup_memory_explicit_layout_description">Let shaders declare explicit layouts for workgroup memory. Disabled by default: some Qualcomm drivers are unstable with it.</string>
<string name="sample_shading_fraction">Sample Shading</string>
<string name="sample_shading_fraction_description">Allows the fragment shader to execute per sample in a multi-sampled fragment instead once per fragment. Improves graphics quality at the cost of some performance.</string>
@@ -1045,7 +1039,6 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Fast</string>
<string name="renderer_accuracy_medium">Balanced</string>
<string name="renderer_accuracy_high">Accurate</string>
<!-- DMA Accuracy -->
+23
View File
@@ -476,6 +476,29 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
path.erase(std::unique(start, path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
const bool absolute = !path.empty() && path[0] == type2;
std::vector<std::string_view> parts;
for (const auto part : SplitPathComponents(path))
{
if (part.empty() || part == ".")
continue;
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back();
else if (part != "..") parts.push_back(part);
}
std::string resolved = absolute ? std::string(1, type2) : std::string{};
for (std::size_t i = 0; i < parts.size(); ++i)
{
if (i != 0)
resolved += type2;
resolved.append(parts[i].data(), parts[i].size());
}
path = std::move(resolved);
return std::string(RemoveTrailingSlash(path));
}
+1 -290
View File
@@ -51,60 +51,14 @@
#endif // ^^^ POSIX ^^^
#include <atomic>
#include <mutex>
#include <random>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/free_region_manager.h"
#include "common/host_memory.h"
#include "common/logging.h"
#include "common/memory_detect.h"
#include "common/settings.h"
#ifdef __ANDROID__
#include <dlfcn.h>
#include <android/hardware_buffer.h>
namespace {
struct NativeHandle {
int version;
int numFds;
int numInts;
int data[1];
};
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
using PFN_AHardwareBuffer_isSupported = int (*)(const AHardwareBuffer_Desc*);
void* NativeWindowLibrary() {
static void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
return lib;
}
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
void* const lib = NativeWindowLibrary();
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
}
PFN_AHardwareBuffer_isSupported ResolveIsSupported() {
void* const lib = NativeWindowLibrary();
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_isSupported>(
dlsym(lib, "AHardwareBuffer_isSupported"));
}
} // namespace
#endif
#if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h>
@@ -121,12 +75,6 @@ namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
static std::atomic<u64> committed_backing_size{};
u64 GetCommittedBackingSize() noexcept {
return committed_backing_size.load(std::memory_order_relaxed);
}
#ifdef _WIN32
// Manually imported for MinGW compatibility
@@ -561,11 +509,6 @@ public:
bool Init() {
long page_size = sysconf(_SC_PAGESIZE);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size);
#ifdef __ANDROID__
if (InitAhbBacking()) {
return InitVirtual();
}
#endif
// Backing memory initialization
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
@@ -611,10 +554,7 @@ public:
return false;
}
return InitVirtual();
}
bool InitVirtual() {
// Virtual memory initialization
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -627,180 +567,6 @@ public:
return true;
}
#ifdef __ANDROID__
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
return AHardwareBuffer_Desc{
.width = static_cast<u32>(len),
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
.stride = 0,
.rfu0 = 0,
.rfu1 = 0,
};
}
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer probe allocation failed");
return false;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
AHardwareBuffer_release(buffer);
return false;
}
const int probe_fd = handle->data[0];
bool ok = true;
const auto try_map = [&](int prot, off_t offset, const char* what) {
if (!ok) {
return;
}
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
if (ptr == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Hardware buffer backing rejects {}: {}", what,
strerror(errno));
ok = false;
return;
}
munmap(ptr, PageAlignment);
};
try_map(PROT_READ | PROT_WRITE, 0, "shared mappings");
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment), "mappings at an offset");
#ifdef ARCHITECTURE_arm64
try_map(PROT_READ | PROT_EXEC, 0, "executable mappings");
#endif
AHardwareBuffer_release(buffer);
return ok;
}
bool InitAhbBacking() {
if (!Settings::values.use_unified_memory.GetValue()) {
return false;
}
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
ResolveGetNativeHandle();
if (get_native_handle == nullptr) {
LOG_WARNING(HW_Memory, "AHardwareBuffer_getNativeHandle is not available");
return false;
}
static const PFN_AHardwareBuffer_isSupported is_supported = ResolveIsSupported();
if (is_supported == nullptr) {
LOG_WARNING(HW_Memory, "AHardwareBuffer_isSupported is not available");
return false;
}
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
if (total_physical != 0 && backing_size > total_physical / 2) {
LOG_WARNING(HW_Memory,
"Hardware buffer backing would commit {} MiB on a {} MiB system, keeping "
"lazily committed memory",
backing_size >> 20, total_physical >> 20);
return false;
}
if (!ProbeAhbBacking(get_native_handle)) {
return false;
}
const auto try_window_size = [&](size_t window_size) -> bool {
const size_t num_windows = (backing_size + window_size - 1) / window_size;
std::vector<AHardwareBuffer*> buffers;
std::vector<int> buffer_fds;
const auto cleanup = [&] {
for (AHardwareBuffer* buffer : buffers) {
AHardwareBuffer_release(buffer);
}
buffers.clear();
buffer_fds.clear();
};
for (size_t i = 0; i < num_windows; ++i) {
const size_t len = (std::min)(window_size, backing_size - i * window_size);
const AHardwareBuffer_Desc desc = MakeBlobDesc(len);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer allocation failed for window {}", i);
cleanup();
return false;
}
buffers.push_back(buffer);
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
cleanup();
return false;
}
const int buffer_fd = handle->data[0];
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
if (buffer_len < static_cast<off_t>(len)) {
LOG_WARNING(HW_Memory, "Hardware buffer descriptor smaller than requested");
cleanup();
return false;
}
buffer_fds.push_back(buffer_fd);
}
u8* const base =
static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (base == MAP_FAILED) {
cleanup();
return false;
}
for (size_t i = 0; i < num_windows; ++i) {
const size_t len = (std::min)(window_size, backing_size - i * window_size);
if (mmap(base + i * window_size, len, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, buffer_fds[i], 0) == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Hardware buffer mmap failed: {}", strerror(errno));
munmap(base, backing_size);
cleanup();
return false;
}
}
backing_base = base;
ahb_windows = std::move(buffers);
ahb_fds = std::move(buffer_fds);
ahb_window_size = window_size;
ahb_backing = true;
committed_backing_size.store(backing_size, std::memory_order_relaxed);
LOG_INFO(HW_Memory,
"Guest memory backed by {} hardware buffer windows of {} MiB, {} MiB committed",
ahb_windows.size(), window_size >> 20, backing_size >> 20);
return true;
};
static constexpr size_t candidate_window_sizes[] = {
1024ULL << 20,
512ULL << 20,
256ULL << 20,
128ULL << 20,
};
for (const size_t candidate : candidate_window_sizes) {
const AHardwareBuffer_Desc window_desc = MakeBlobDesc(candidate);
if (is_supported(&window_desc) == 0) {
LOG_DEBUG(HW_Memory, "Allocator rejects {} MiB hardware buffer windows",
candidate >> 20);
continue;
}
if (try_window_size(candidate)) {
return true;
}
LOG_WARNING(HW_Memory, "Could not back guest memory with {} MiB windows",
candidate >> 20);
}
return false;
}
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
return ahb_windows;
}
size_t AhbWindowSize() const noexcept {
return ahb_backing ? ahb_window_size : 0;
}
#endif
~Impl() {
Release();
}
@@ -821,26 +587,6 @@ public:
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute))
prot_flags |= PROT_EXEC;
#endif
#ifdef __ANDROID__
if (ahb_backing) {
size_t voff = virtual_offset;
size_t hoff = host_offset;
size_t remaining = length;
while (remaining > 0) {
const size_t window = hoff / ahb_window_size;
const size_t local = hoff % ahb_window_size;
const size_t chunk = (std::min)(remaining, ahb_window_size - local);
void* const ret =
mmap(virtual_base + voff, chunk, prot_flags, MAP_SHARED | MAP_FIXED,
ahb_fds[window], static_cast<off_t>(local));
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
voff += chunk;
hoff += chunk;
remaining -= chunk;
}
return;
}
#endif
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
@@ -910,18 +656,6 @@ private:
int ret = close(fd);
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
}
#ifdef __ANDROID__
for (AHardwareBuffer* buffer : ahb_windows) {
AHardwareBuffer_release(buffer);
}
ahb_windows.clear();
ahb_fds.clear();
if (ahb_backing) {
committed_backing_size.store(0, std::memory_order_relaxed);
ahb_backing = false;
}
#endif
}
void AdjustMap(size_t* virtual_offset, size_t* length) {
@@ -947,13 +681,6 @@ private:
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{};
#ifdef __ANDROID__
bool ahb_backing{};
std::vector<AHardwareBuffer*> ahb_windows;
std::vector<int> ahb_fds;
size_t ahb_window_size{};
#endif
};
#endif // ^^^ POSIX ^^^
@@ -1040,22 +767,6 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
std::memset(backing_base + physical_offset, fill_value, length);
}
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
#else
return {};
#endif
}
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindowSize() : 0;
#else
return 0;
#endif
}
void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) {
-13
View File
@@ -8,17 +8,12 @@
#include <memory>
#include <optional>
#include <span>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Common {
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
enum class MemoryPermission : u32 {
Read = 1 << 0,
Write = 1 << 1,
@@ -67,14 +62,6 @@ public:
return backing_base;
}
[[nodiscard]] size_t BackingSize() const noexcept {
return backing_size;
}
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
[[nodiscard]] u8* VirtualBasePointer() noexcept {
return virtual_base;
}
-13
View File
@@ -587,9 +587,6 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
Category::RendererHacks};
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
GpuUnswizzleSize::Large,
"gpu_unswizzle_texture_size",
@@ -638,16 +635,6 @@ struct Values {
#endif
"vertex_input_dynamic_state", Category::RendererExtensions};
SwitchableSetting<bool> dynamic_rendering{linkage, true, "dynamic_rendering",
Category::RendererExtensions};
SwitchableSetting<bool> workgroup_memory_explicit_layout{
linkage, false, "workgroup_memory_explicit_layout", Category::RendererExtensions};
SwitchableSetting<s32, true> pipeline_worker_count{
linkage, 2, 2, 8, "pipeline_worker_count", Category::RendererAdvanced,
Specialization::Scalar};
Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
Category::RendererDebug};
+20 -139
View File
@@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -40,110 +39,6 @@
#include <unistd.h>
#endif
#ifdef __ANDROID__
#include <sys/resource.h>
#include <algorithm>
#include <fstream>
#include <utility>
#include <vector>
namespace {
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_AUDIO = -19;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_AUDIO = -16;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_DISPLAY = -8;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DISPLAY = -4;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_FOREGROUND = -2;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_MORE_FAVORABLE = -1;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DEFAULT = 0;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LESS_FAVORABLE = 1;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_BACKGROUND = 10;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LOWEST = 19;
constexpr size_t ANDROID_MINIMUM_PERFORMANCE_CORES = 4;
cpu_set_t ComputePerformanceCoreMask() {
cpu_set_t mask;
CPU_ZERO(&mask);
cpu_set_t allowed;
CPU_ZERO(&allowed);
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
return mask;
}
std::vector<std::pair<long, int>> cores;
const int total = static_cast<int>(std::thread::hardware_concurrency());
for (int cpu = 0; cpu < total; ++cpu) {
if (!CPU_ISSET(cpu, &allowed)) {
continue;
}
long max_frequency = 0;
std::ifstream file("/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
"/cpufreq/cpuinfo_max_freq");
if (!file || !(file >> max_frequency) || max_frequency <= 0) {
CPU_ZERO(&mask);
return mask;
}
cores.emplace_back(max_frequency, cpu);
}
if (cores.empty()) {
return mask;
}
std::sort(cores.begin(), cores.end(),
[](const auto& lhs, const auto& rhs) { return lhs.first > rhs.first; });
size_t taken = 0;
long cluster_frequency = cores.front().first;
for (const auto& [frequency, cpu] : cores) {
if (frequency != cluster_frequency) {
if (taken >= ANDROID_MINIMUM_PERFORMANCE_CORES) {
break;
}
cluster_frequency = frequency;
}
CPU_SET(cpu, &mask);
++taken;
}
return mask;
}
const cpu_set_t& PerformanceCoreMask() {
static const cpu_set_t mask = ComputePerformanceCoreMask();
return mask;
}
cpu_set_t ComputeEfficiencyCoreMask() {
cpu_set_t mask;
CPU_ZERO(&mask);
const cpu_set_t& performance = PerformanceCoreMask();
if (CPU_COUNT(&performance) == 0) {
return mask;
}
cpu_set_t allowed;
CPU_ZERO(&allowed);
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
return mask;
}
const int total = static_cast<int>(std::thread::hardware_concurrency());
for (int cpu = 0; cpu < total; ++cpu) {
if (CPU_ISSET(cpu, &allowed) && !CPU_ISSET(cpu, &performance)) {
CPU_SET(cpu, &mask);
}
}
return mask;
}
const cpu_set_t& EfficiencyCoreMask() {
static const cpu_set_t mask = ComputeEfficiencyCoreMask();
return mask;
}
} // Anonymous namespace
#endif
#include "common/cpu_features.h"
#ifdef ARCHITECTURE_x86_64
#ifdef _MSC_VER
@@ -183,21 +78,6 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
}
}();
set_thread_priority(find_thread(NULL), priority);
#elif defined(__ANDROID__)
const int nice_value = [&]() {
switch (new_priority) {
case ThreadPriority::Low: return ANDROID_THREAD_PRIORITY_BACKGROUND;
case ThreadPriority::Normal: return ANDROID_THREAD_PRIORITY_DEFAULT;
case ThreadPriority::High: return ANDROID_THREAD_PRIORITY_DISPLAY;
case ThreadPriority::VeryHigh: return ANDROID_THREAD_PRIORITY_URGENT_DISPLAY;
case ThreadPriority::Critical: return ANDROID_THREAD_PRIORITY_AUDIO;
default: return ANDROID_THREAD_PRIORITY_DEFAULT;
}
}();
if (setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), nice_value) != 0) {
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
GetLastErrorMsg());
}
#else
pthread_t this_thread = pthread_self();
const auto scheduling_type = SCHED_OTHER;
@@ -252,28 +132,29 @@ void SetCurrentThreadName(const char* name) {
#endif
}
void SetCurrentThreadToPerformanceCores() {
void PinCurrentThreadToPerformanceCore(size_t core_id) {
ASSERT(core_id < 4);
// If we set a flag for a CPU that doesn't exist, the thread may not be allowed to
// run in ANY processor!
auto const total_cores = std::thread::hardware_concurrency();
if (core_id < total_cores) {
#if defined(__ANDROID__)
const cpu_set_t& mask = PerformanceCoreMask();
if (CPU_COUNT(&mask) == 0) {
return;
}
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
LOG_DEBUG(Common, "Could not restrict thread to performance cores: {}", GetLastErrorMsg());
}
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(core_id, &set);
sched_setaffinity(pthread_self(), sizeof(set), &set);
#elif defined(__linux__) || defined(__FreeBSD__)
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(core_id, &set);
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
#elif defined(_WIN32)
DWORD set = 1UL << core_id;
SetThreadAffinityMask(GetCurrentThread(), set);
#else
// No pin functionality implemented
#endif
}
void SetCurrentThreadToEfficiencyCores() {
#if defined(__ANDROID__)
const cpu_set_t& mask = EfficiencyCoreMask();
if (CPU_COUNT(&mask) == 0) {
return;
}
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
LOG_DEBUG(Common, "Could not restrict thread to efficiency cores: {}", GetLastErrorMsg());
}
#endif
}
#ifdef ARCHITECTURE_x86_64
+1 -7
View File
@@ -99,14 +99,8 @@ enum class ThreadPriority : u32 {
Critical = 4,
};
enum class ThreadPlacement : u32 {
Default = 0,
Background = 1,
};
void SetCurrentThreadPriority(ThreadPriority new_priority);
void SetCurrentThreadName(const char* name);
void SetCurrentThreadToPerformanceCores();
void SetCurrentThreadToEfficiencyCores();
void PinCurrentThreadToPerformanceCore(size_t core_id);
} // namespace Common
+3 -8
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -37,15 +37,10 @@ class StatefulThreadWorker {
using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
public:
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {},
ThreadPlacement placement = ThreadPlacement::Default)
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
: workers_queued{num_workers}, thread_name{std::move(name)} {
const auto lambda = [this, func, placement](std::stop_token stop_token) {
const auto lambda = [this, func](std::stop_token stop_token) {
Common::SetCurrentThreadName(thread_name.c_str());
if (placement == ThreadPlacement::Background) {
Common::SetCurrentThreadPriority(ThreadPriority::Low);
Common::SetCurrentThreadToEfficiencyCores();
}
{
[[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
while (!stop_token.stop_requested()) {
+1 -3
View File
@@ -157,8 +157,6 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info,
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
constexpr size_t NCE_WRITE_FAULT_CLUSTER_PAGES = 4;
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto* info = static_cast<siginfo_t*>(raw_info);
@@ -167,7 +165,7 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi
const Common::ProcessAddress addr =
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE * NCE_WRITE_FAULT_CLUSTER_PAGES)) {
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
// We handled the access successfully and are returning to guest code.
return true;
}
+18 -1
View File
@@ -145,7 +145,14 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
// MRS Xn, CNTFRQ_EL0
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
UNREACHABLE();
bool pre_buffer = false;
auto ret = AddRelocations(pre_buffer);
if (pre_buffer) {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c_pre);
} else {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c);
}
continue;
}
// MSR TPIDR_EL0, Xn
@@ -577,6 +584,16 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
this->BranchToModule(module_dest);
}
void Patcher::WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
cg.MOV(dest_reg, Common::WallClock::CNTFRQ);
// Jump back to the instruction after the emulated MRS.
if (&cg == &c_pre)
this->BranchToModulePre(module_dest);
else
this->BranchToModule(module_dest);
}
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
#if defined(HAS_NCE)
static Common::WallClock clock(false, 1);
+2
View File
@@ -80,6 +80,7 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& code, oaknut::Label& save_ctx, oaknut::Label& load_ctx);
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
// Convenience wrappers using default code generator
@@ -90,6 +91,7 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { WriteSvcTrampoline(module_dest, svc_id, c, m_save_context, m_load_context); }
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg) { WriteMrsHandler(module_dest, dest_reg, src_reg, c); }
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { WriteMsrHandler(module_dest, src_reg, c); }
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntfrqHandler(module_dest, dest_reg, c); }
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntpctHandler(module_dest, dest_reg, c); }
private:
+1 -5
View File
@@ -118,7 +118,6 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -146,8 +145,7 @@ struct System::Impl {
!device_memory.has_value() ||
is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
Settings::MemoryLayout::Memory_4Gb) ||
unified_memory != Settings::values.use_unified_memory.GetValue();
Settings::MemoryLayout::Memory_4Gb);
if (!must_reinitialize) {
return;
@@ -158,7 +156,6 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
Initialize(system);
}
@@ -506,7 +503,6 @@ struct System::Impl {
std::atomic_bool is_powered_on{};
bool is_multicore : 1 = false;
bool extended_memory_layout : 1 = false;
bool unified_memory : 1 = false;
bool exit_locked : 1 = false;
bool exit_requested : 1 = false;
bool nvdec_active : 1 = false;
+6 -1
View File
@@ -174,7 +174,12 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
Common::SetCurrentThreadToPerformanceCores();
#ifdef __ANDROID__
// Aimed specifically for Snapdragon 8 Elite devices
// This kills performance on desktop, but boosts perf for UMA devices
// like the S8E. Mediatek and Mali likely won't suffer.
Common::PinCurrentThreadToPerformanceCore(core);
#endif
auto& data = core_data[core];
data.host_context = Common::Fiber::ThreadToFiber();
-30
View File
@@ -20,8 +20,6 @@
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -97,26 +95,6 @@ public:
ApplyOpOnPAddr(address, buffer, operation);
}
u8* GetPhysicalBase() noexcept {
return reinterpret_cast<u8*>(physical_base);
}
const u8* GetPhysicalBase() const noexcept {
return reinterpret_cast<const u8*>(physical_base);
}
size_t GetPhysicalSize() const noexcept {
return physical_size;
}
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
return ahb_windows;
}
size_t GetBackingHardwareBufferWindowSize() const noexcept {
return ahb_window_size;
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = PAddr(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
@@ -148,10 +126,6 @@ public:
// New batch API to update multiple ranges with a single lock acquisition.
void UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta);
void UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta);
[[nodiscard]] bool IsRegionTextureCached(DAddr addr, size_t size) const noexcept;
private:
struct TranslationEntry {
DAddr guest_page{};
@@ -197,9 +171,6 @@ private:
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base;
const size_t physical_size;
const std::span<AHardwareBuffer* const> ahb_windows;
const size_t ahb_window_size;
DeviceInterface* device_inter;
struct TrackedEntry {
@@ -263,7 +234,6 @@ private:
(1ULL << (device_virtual_bits - page_bits)) / subentries;
using CachedPages = std::array<CounterEntry, num_counter_entries>;
std::unique_ptr<CachedPages> cached_pages;
std::unique_ptr<CachedPages> texture_cached_pages;
Common::RangeMutex counter_guard;
std::mutex mapping_guard;
-26
View File
@@ -171,16 +171,12 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
, physical_size{device_memory_.buffer.BackingSize()}
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
, device_inter{nullptr}
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
{
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>();
texture_cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
@@ -629,28 +625,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
UpdatePagesCachedCountNoLock(addr, size, delta);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta) {
Common::ScopedRangeLock lk(counter_guard, addr, size);
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
CounterAtomicType& count = texture_cached_pages->at(page >> subentries_shift).Count(page);
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
}
}
template <typename Traits>
bool DeviceMemoryManager<Traits>::IsRegionTextureCached(DAddr addr, size_t size) const noexcept {
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
if (texture_cached_pages->at(page >> subentries_shift).Count(page).load(
std::memory_order_acquire) != 0) {
return true;
}
}
return false;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
if (ranges.empty()) {
+16
View File
@@ -984,6 +984,22 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
return removed_data;
}
bool RegisteredCache::Delete(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const bool is_file = dir->GetFileRelative(path) != nullptr;
const bool is_dir = dir->GetDirectoryRelative(path) != nullptr;
if (is_file) {
return dir->DeleteFile(path);
}
if (is_dir) {
return dir->DeleteSubdirectoryRecursive(path);
}
return true;
}
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {
+1
View File
@@ -188,6 +188,7 @@ public:
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id) const;
bool Delete(const NcaID& id) const;
private:
template <typename T>
+17 -5
View File
@@ -35,6 +35,16 @@ namespace {
constexpr size_t MaxOpenFiles = 8192;
bool IsWithinRoot(std::string_view root, std::string_view full_path) {
if (root.empty())
return true;
if (full_path.size() < root.size() || full_path.substr(0, root.size()) != root)
return false;
return full_path.size() == root.size() || full_path[root.size()] == '/' || full_path[root.size()] == '\\';
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
switch (mode) {
case OpenMode::Read:
@@ -403,7 +413,8 @@ RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr;
}
return base.OpenFile(full_path, perms);
@@ -411,7 +422,8 @@ VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) co
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
if (!FS::Exists(full_path) || !FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr;
}
return base.OpenDirectory(full_path, perms);
@@ -427,7 +439,7 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::CreateParentDirs(full_path)) {
if (!FS::CreateParentDirs(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr;
}
return base.CreateFile(full_path, perms);
@@ -440,7 +452,7 @@ VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_p
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
return base.DeleteDirectory(full_path);
return FS::RemoveDirRecursively(full_path);
}
std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
@@ -506,7 +518,7 @@ VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + '/').append(name);
return base.DeleteDirectory(subdir_path);
return FS::RemoveDir(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
@@ -57,7 +57,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
{64, nullptr, "SetInputDetectionSourceSet"},
{65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{67, D<&ISelfController::IsIlluminanceAvailable>, "IsIlluminanceAvailable"},
{68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"},
{69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
@@ -347,6 +347,12 @@ Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled
R_SUCCEED();
}
Result ISelfController::IsIlluminanceAvailable(Out<bool> out_is_illuminance_available) {
LOG_WARNING(Service_AM, "(stubbed)");
*out_is_illuminance_available = false;
R_SUCCEED();
}
Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -60,6 +60,7 @@ private:
Result ReportUserIsActive();
Result SetAutoSleepDisabled(bool is_auto_sleep_disabled);
Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled);
Result IsIlluminanceAvailable(Out<bool> out_is_illuminance_available);
Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy);
Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value);
Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+42 -10
View File
@@ -43,6 +43,16 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
return base->GetDirectoryRelative(dir_name);
}
static std::string_view GetGuestParentPath(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? std::string_view{} : path.substr(0, name_index);
}
static std::string_view GetGuestFilename(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? path : path.substr(name_index + 1);
}
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
: backing(std::move(backing_)) {}
@@ -83,11 +93,12 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
return ResultSuccess;
}
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
const auto filename = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (filename.empty() || dir == nullptr || dir->GetFile(filename) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
if (!dir->DeleteFile(filename)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -97,7 +108,9 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
if (GetDirectoryRelativeWrapped(backing, path) != nullptr) {
return FileSys::ResultPathAlreadyExists;
}
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
// CreateDirectory should return PathNotFound if the parent directory does not exist.
// This is here temporarily in order to have UMM "work" in the meantime.
@@ -117,8 +130,19 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
FileSys::VirtualDir target{};
if (!dirname.empty() && dir != nullptr) {
target = dir->GetSubdirectory(dirname);
}
if (target == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!target->GetFiles().empty() || !target->GetSubdirectories().empty()) {
return ResultUnknown;
}
if (!dir->DeleteSubdirectory(dirname)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -127,8 +151,12 @@ Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteSubdirectoryRecursive(dirname)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -137,9 +165,13 @@ Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string&
Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
const std::string sanitized_path(Common::FS::SanitizePath(path));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
const auto dirname = GetGuestFilename(sanitized_path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(sanitized_path));
if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->CleanSubdirectoryRecursive(dirname)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
+7 -1
View File
@@ -237,7 +237,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+
{3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+
{3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+
{3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+
{3150, C<&IHidServer::SetMouseLibraryVersion>, "SetMouseLibraryVersion"}, //21.0.0+
// What? -- {12010, nullptr, "SetButtonConfigLeft"},
};
// clang-format on
@@ -1471,6 +1471,12 @@ Result IHidServer::SetTouchScreenResolution(u32 width, u32 height,
R_SUCCEED();
}
Result IHidServer::SetMouseLibraryVersion(ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;
+2 -1
View File
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -263,6 +263,7 @@ private:
Result IsFirmwareUpdateNeededForNotification(Out<bool> out_is_firmware_update_needed,
s32 unknown, ClientAppletResourceUserId aruid);
Result SetTouchScreenResolution(u32 width, u32 height, ClientAppletResourceUserId aruid);
Result SetMouseLibraryVersion(ClientAppletResourceUserId aruid);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;
+297 -2
View File
@@ -1,9 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <memory>
#include <vector>
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/api_version.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/server_manager.h"
@@ -88,6 +98,268 @@ public:
}
};
class IContentStorage final : public ServiceFramework<IContentStorage> {
public:
explicit IContentStorage(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentStorage"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentStorage::GeneratePlaceHolderId, "GeneratePlaceHolderId"},
{1, &IContentStorage::CreatePlaceHolder, "CreatePlaceHolder"},
{2, &IContentStorage::DeletePlaceHolder, "DeletePlaceHolder"},
{4, &IContentStorage::WritePlaceHolder, "WritePlaceHolder"},
{5, &IContentStorage::Register, "Register"},
{6, &IContentStorage::Delete, "Delete"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GeneratePlaceHolderId(HLERequestContext& ctx) {
LOG_DEBUG(Service_NCM, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(FileSys::PlaceholderCache::Generate());
}
void CreatePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
const auto size = rp.Pop<s64>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && size >= 0 &&
(placeholder_cache->Exists(placeholder_id) ||
placeholder_cache->Create(placeholder_id, static_cast<u64>(size)));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, size={}", static_cast<u32>(storage),
size);
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, size={}", static_cast<u32>(storage),
size);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void DeletePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr &&
(!placeholder_cache->Exists(placeholder_id) || placeholder_cache->Delete(placeholder_id));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void WritePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
const auto offset = rp.Pop<u64>();
const auto data = ctx.ReadBuffer();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const std::vector<u8> write_data{data.begin(), data.end()};
const bool succeeded =
placeholder_cache != nullptr &&
placeholder_cache->Write(placeholder_id, offset, write_data);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Register(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
auto& fsc = system.GetFileSystemController();
auto* const placeholder_cache = fsc.GetPlaceholderCacheForStorage(storage);
auto* const registered_cache = fsc.GetRegisteredCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && registered_cache != nullptr &&
placeholder_cache->Register(registered_cache, placeholder_id, content_id);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Delete(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto content_id = rp.PopRaw<FileSys::NcaID>();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool succeeded = registered_cache != nullptr && registered_cache->Delete(content_id);
if (succeeded) {
registered_cache->Refresh();
}
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
FileSys::StorageId storage;
};
class IContentMetaDatabase final : public ServiceFramework<IContentMetaDatabase> {
public:
explicit IContentMetaDatabase(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentMetaDatabase"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentMetaDatabase::Set, "Set"},
{2, &IContentMetaDatabase::Remove, "Remove"},
{8, &IContentMetaDatabase::Has, "Has"},
{15, &IContentMetaDatabase::Commit, "Commit"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
struct ContentMetaKey {
u64 id;
u32 version;
FileSys::TitleType type;
u8 install_type;
std::array<u8, 2> padding;
};
static_assert(sizeof(ContentMetaKey) == 0x10);
void Set(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const auto entry_matches = [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
};
if (std::find_if(entries.begin(), entries.end(), entry_matches) == entries.end()) {
entries.push_back(key);
}
LOG_DEBUG(Service_NCM,
"called, storage_id={}, title_id={:016X}, version={}, type={}, size={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Remove(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
std::erase_if(entries, [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
});
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Has(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const bool has_pending =
std::find_if(entries.begin(), entries.end(), [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version &&
entry.type == key.type && entry.install_type == key.install_type;
}) != entries.end();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool has_registered =
registered_cache != nullptr &&
registered_cache->HasEntry(key.id, FileSys::ContentRecordType::Meta);
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}, has={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
has_pending || has_registered);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(has_pending || has_registered);
}
void Commit(HLERequestContext& ctx) {
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
if (registered_cache != nullptr) {
registered_cache->Refresh();
}
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
FileSys::StorageId storage;
std::vector<ContentMetaKey> entries;
};
class LR final : public ServiceFramework<LR> {
public:
explicit LR(Core::System& system_) : ServiceFramework{system_, "lr"} {
@@ -113,8 +385,8 @@ public:
{1, nullptr, "CreateContentMetaDatabase"},
{2, nullptr, "VerifyContentStorage"},
{3, nullptr, "VerifyContentMetaDatabase"},
{4, nullptr, "OpenContentStorage"},
{5, nullptr, "OpenContentMetaDatabase"},
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
{6, nullptr, "CloseContentStorageForcibly"},
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
{8, nullptr, "CleanupContentMetaDatabase"},
@@ -130,6 +402,29 @@ public:
RegisterHandlers(functions);
}
private:
void OpenContentStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentStorage>(ctx, system, storage_id);
}
void OpenContentMetaDatabase(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentMetaDatabase>(ctx, system, storage_id);
}
};
void LoopProcess(Core::System& system) {
@@ -9,6 +9,7 @@
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/file_sys/content_archive.h"
@@ -19,6 +20,7 @@
#include "core/launch_timestamp_cache.h"
#include <algorithm>
#include <cstring>
#include <vector>
namespace Service::NS {
@@ -36,14 +38,14 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{1, nullptr, "GenerateApplicationRecordCount"},
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
{3, nullptr, "GetApplicationViewDeprecated"},
{4, nullptr, "DeleteApplicationEntity"},
{5, nullptr, "DeleteApplicationCompletely"},
{4, D<&IApplicationManagerInterface::DeleteApplicationEntity>, "DeleteApplicationEntity"},
{5, D<&IApplicationManagerInterface::DeleteApplicationCompletely>, "DeleteApplicationCompletely"},
{6, nullptr, "IsAnyApplicationEntityRedundant"},
{7, nullptr, "DeleteRedundantApplicationEntity"},
{8, nullptr, "IsApplicationEntityMovable"},
{9, nullptr, "MoveApplicationEntity"},
{11, nullptr, "CalculateApplicationOccupiedSize"},
{16, nullptr, "PushApplicationRecord"},
{16, &IApplicationManagerInterface::PushApplicationRecord, "PushApplicationRecord"},
{17, nullptr, "ListApplicationRecordContentMeta"},
{19, nullptr, "LaunchApplicationOld"},
{21, nullptr, "GetApplicationContentPath"},
@@ -643,6 +645,27 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationEntity(u64 application_id) {
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->RemoveExistingEntry(application_id);
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->RemoveExistingEntry(application_id);
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationCompletely(u64 application_id) {
R_RETURN(DeleteApplicationEntity(application_id));
}
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
@@ -843,6 +866,29 @@ Result IApplicationManagerInterface::Unknown4053() {
R_SUCCEED();
}
void IApplicationManagerInterface::PushApplicationRecord(HLERequestContext& ctx) {
const auto record = ctx.ReadBuffer();
u64 application_id{};
if (record.size() >= sizeof(application_id)) {
std::memcpy(&application_id, record.data(), sizeof(application_id));
}
LOG_DEBUG(Service_NS, "called, application_id={:016X}, size={}", application_id, record.size());
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
@@ -56,6 +56,8 @@ public:
Result ResumeAll();
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
u64 process_id);
Result DeleteApplicationEntity(u64 application_id);
Result DeleteApplicationCompletely(u64 application_id);
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
FileSys::StorageId storage_id);
Result TouchApplication(u64 application_id);
@@ -74,6 +76,7 @@ public:
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
u64 application_id);
void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx);
private:
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,9 +31,9 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
static const FunctionInfo functions[] = {
{0, &SPL::GetConfig, "GetConfig"},
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{2, &SPL::GenerateAesKek, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{4, &SPL::GenerateAesKey, "GenerateAesKey"},
{5, &SPL::SetConfig, "SetConfig"},
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
{11, &SPL::IsDevelopment, "IsDevelopment"},
+30
View File
@@ -59,6 +59,36 @@ void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
rb.Push(ResultSecureMonitorNotImplemented);
}
void Module::Interface::GenerateAesKek(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
const auto generation = rp.Pop<u32>();
const auto option = rp.Pop<u32>();
LOG_WARNING(Service_SPL, "(STUBBED) called, generation={:#x}, option={:#x}", generation,
option);
AccessKey access_key{};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(access_key);
}
void Module::Interface::GenerateAesKey(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto access_key = rp.PopRaw<AccessKey>();
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
LOG_WARNING(Service_SPL, "(STUBBED) called");
AesKey aes_key{};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(aes_key);
}
void Module::Interface::SetConfig(HLERequestContext& ctx) {
UNIMPLEMENTED_MSG("SetConfig is not implemented!");
+5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -25,6 +28,8 @@ public:
// General
void GetConfig(HLERequestContext& ctx);
void ModularExponentiate(HLERequestContext& ctx);
void GenerateAesKek(HLERequestContext& ctx);
void GenerateAesKey(HLERequestContext& ctx);
void SetConfig(HLERequestContext& ctx);
void GenerateRandomBytes(HLERequestContext& ctx);
void IsDevelopment(HLERequestContext& ctx);
@@ -230,11 +230,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
tr("May reduce shader stutter."));
INSERT(Settings, use_unified_memory, tr("Enable unified memory access (UMA)"),
tr("Lets the GPU write buffer readbacks directly into guest memory."));
INSERT(Settings, pipeline_worker_count, tr("Pipeline Worker Threads"),
tr("Number of threads used to build Vulkan pipelines.\n"
"Higher values speed up compilation at the cost of heat and power."));
INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"),
tr("Overclocks the emulated GPU to increase dynamic resolution and render "
"distance.\nUse 256 for maximal performance and 512 for maximal graphics fidelity."));
@@ -292,12 +287,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, vertex_input_dynamic_state, tr("Vertex Input Dynamic State"),
tr("Enables vertex input dynamic state feature for better quality and performance."));
INSERT(Settings, dynamic_rendering, tr("Dynamic Rendering"),
tr("Renders without render pass and framebuffer objects.\n"
"Results vary by driver: some gain performance, others lose it."));
INSERT(Settings, workgroup_memory_explicit_layout, QString(), QString());
INSERT(
Settings, sample_shading, tr("Sample Shading"),
tr("Allows the fragment shader to execute per sample in a multi-sampled fragment "
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -665,8 +665,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU3
const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane);
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction);
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
ScalarU32 swizzle);
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -100,24 +97,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, Sca
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
}
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane) {
const Register ret{ctx.reg_alloc.Define(inst)};
ctx.Add("AND.U RC.x,{}.threadid,~3;"
"AND.U RC.y,{},3;"
"OR.U RC.x,RC.x,RC.y;"
"SHFIDX.U {},{},RC.x,0x1C03;"
"MOV.U {}.x,{}.y;",
ctx.stage_name, lane, ret, value, ret, ret);
}
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction) {
const Register ret{ctx.reg_alloc.Define(inst)};
ctx.Add("ADD.U RC.x,{},1;"
"SHFXOR.U {},{},RC.x,0x1C03;"
"MOV.U {}.x,{}.y;",
direction, ret, value, ret, ret);
}
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
ScalarU32 swizzle) {
const auto ret{ctx.reg_alloc.Define(inst)};
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -743,10 +743,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view index, std::string_view clamp,
std::string_view segmentation_mask);
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view lane);
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view direction);
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
std::string_view swizzle);
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -203,18 +200,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view val
ctx.AddU32("{}=shfl_in_bounds?shfl_result:{};", inst, value);
}
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view lane) {
const auto src_thread_id{fmt::format("(({}&~3)|({}& 3))", THREAD_ID, lane)};
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
}
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view direction) {
const auto src_thread_id{fmt::format("({}^({}+1))", THREAD_ID, direction)};
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
}
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
std::string_view swizzle) {
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
@@ -322,11 +322,6 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
if (ctx.runtime_info.force_early_z) {
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
}
if (ctx.profile.support_shader_quad_control && program.info.uses_quad_shuffles) {
ctx.AddExtension("SPV_KHR_quad_control");
ctx.AddCapability(spv::Capability::QuadControlKHR);
ctx.AddExecutionMode(main, spv::ExecutionMode::RequireFullQuadsKHR);
}
break;
default:
throw NotImplementedException("Stage {}", program.stage);
@@ -448,12 +443,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
}
}
if (info.uses_quad_shuffles) {
if (profile.support_quad_shuffles) {
ctx.AddCapability(spv::Capability::GroupNonUniformQuad);
}
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
}
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
ctx.AddCapability(spv::Capability::Int64Atomics);
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -622,8 +622,6 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
Id segmentation_mask);
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
Id segmentation_mask);
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane);
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction);
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
@@ -260,21 +260,6 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
return SelectValue(ctx, in_range, value, src_thread_id);
}
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
if (ctx.profile.support_quad_shuffles) {
return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
}
const Id base{ctx.OpBitwiseAnd(ctx.U32[1], GetThreadId(ctx), ctx.Const(~3u))};
const Id local_lane{ctx.OpBitwiseAnd(ctx.U32[1], lane, ctx.Const(3u))};
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], base, local_lane)};
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
}
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction) {
const Id xor_mask{ctx.OpIAdd(ctx.U32[1], direction, ctx.Const(1u))};
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, xor_mask);
}
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
const Id three{ctx.Const(3U)};
Id mask{GetThreadId(ctx)};
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -2100,14 +2100,6 @@ U32 IREmitter::ShuffleButterfly(const IR::U32& value, const IR::U32& index, cons
return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask);
}
U32 IREmitter::QuadBroadcast(const IR::U32& value, const IR::U32& lane) {
return Inst<U32>(Opcode::QuadBroadcast, value, lane);
}
U32 IREmitter::QuadSwap(const IR::U32& value, const IR::U32& direction) {
return Inst<U32>(Opcode::QuadSwap, value, direction);
}
F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) {
return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle);
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -394,8 +394,6 @@ public:
const IR::U32& seg_mask);
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
const IR::U32& clamp, const IR::U32& seg_mask);
[[nodiscard]] U32 QuadBroadcast(const IR::U32& value, const IR::U32& lane);
[[nodiscard]] U32 QuadSwap(const IR::U32& value, const IR::U32& direction);
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
FpControl control = {});
@@ -10,7 +10,7 @@ namespace Shader::IR {
namespace Detail {
OpcodeMeta META_TABLE[] = {
OpcodeMeta META_TABLE[532] = {
#define OPCODE(name_token, type_token, ...) \
{ \
.name{#name_token}, \
@@ -21,7 +21,7 @@ OpcodeMeta META_TABLE[] = {
#undef OPCODE
};
u8 NUM_ARGS[] = {
u8 NUM_ARGS[532] = {
#define OPCODE(name_token, type_token, ...) u8(CalculateNumArgsOf(Opcode::name_token)),
#include "opcodes.inc"
#undef OPCODE
+2 -2
View File
@@ -57,12 +57,12 @@ static constexpr Type F64x2{Type::F64x2};
static constexpr Type F64x3{Type::F64x3};
static constexpr Type F64x4{Type::F64x4};
extern OpcodeMeta META_TABLE[];
extern OpcodeMeta META_TABLE[532];
constexpr size_t CalculateNumArgsOf(Opcode op) noexcept {
const auto& arg_types = META_TABLE[size_t(op)].arg_types;
return size_t(std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
}
extern u8 NUM_ARGS[];
extern u8 NUM_ARGS[532];
} // namespace Detail
/// Get return type of an opcode
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -582,8 +579,6 @@ OPCODE(ShuffleIndex, U32, U32,
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
OPCODE(QuadBroadcast, U32, U32, U32, )
OPCODE(QuadSwap, U32, U32, U32, )
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
OPCODE(DPdxFine, F32, F32, )
OPCODE(DPdyFine, F32, F32, )
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -39,6 +39,17 @@ IR::U32 scaleIndex(IR::IREmitter& ir, IR::U32 index, Shift shift) {
}
}
IR::U32 skewBytes(IR::IREmitter& ir, SZ sizeRead) {
const IR::U32 lane = ir.LaneId();
switch (sizeRead) {
case SZ::U8: return lane;
case SZ::U16: return ir.ShiftLeftLogical(lane, ir.Imm32(1));
case SZ::U32:
case SZ::F32: return ir.ShiftLeftLogical(lane, ir.Imm32(2));
default: UNREACHABLE();
}
}
} // Anonymous namespace
void TranslatorVisitor::ISBERD(u64 insn) {
@@ -57,41 +68,61 @@ void TranslatorVisitor::ISBERD(u64 insn) {
BitField<47, 2, Shift> shift;
} const isberd{insn};
if (isberd.skew != 0) {
throw NotImplementedException("ISBERD SKEW");
}
if (isberd.o != 0) {
throw NotImplementedException("ISBERD O");
}
if (isberd.sz.Value() > SZ::F32) {
throw NotImplementedException("ISBERD SZ {}",
static_cast<u64>(isberd.sz.Value()));
}
if (isberd.shift.Value() > Shift::B32) {
throw NotImplementedException("ISBERD Shift {}",
static_cast<u64>(isberd.shift.Value()));
IR::U32 index{};
if (isberd.src_reg_num.Value() == 0xFF) {
index = ir.Imm32(isberd.imm.Value());
} else {
const IR::U32 scaledIndex = scaleIndex(ir, X(isberd.src_reg.Value()), isberd.shift.Value());
index = ir.IAdd(scaledIndex, ir.Imm32(isberd.imm.Value()));
}
switch (isberd.mode.Value()) {
case Mode::Default:
X(isberd.dest_reg.Value(), X(isberd.src_reg.Value()));
return;
case Mode::Attr: {
IR::U32 offset{};
if (isberd.src_reg_num.Value() == 0xFF) {
offset = ir.Imm32(isberd.imm.Value());
} else {
const IR::U32 index{
scaleIndex(ir, X(isberd.src_reg.Value()), isberd.shift.Value())};
offset = ir.IAdd(index, ir.Imm32(isberd.imm.Value()));
if (isberd.o.Value()) {
if (isberd.skew.Value()) {
index = ir.IAdd(index, skewBytes(ir, isberd.sz.Value()));
}
X(isberd.dest_reg.Value(), ir.BitCast<IR::U32>(ir.GetAttributeIndexed(offset)));
const IR::U64 index64 = ir.UConvert(64, index);
IR::U32 globalLoaded{};
switch (isberd.sz.Value()) {
case SZ::U8: globalLoaded = ir.LoadGlobalU8 (index64); break;
case SZ::U16: globalLoaded = ir.LoadGlobalU16(index64); break;
case SZ::U32:
case SZ::F32: globalLoaded = ir.LoadGlobal32(index64); break;
default: UNREACHABLE();
}
X(isberd.dest_reg.Value(), globalLoaded);
return;
}
default:
throw NotImplementedException("ISBERD Mode {}",
static_cast<u64>(isberd.mode.Value()));
if (isberd.mode.Value() != Mode::Default) {
if (isberd.skew.Value()) {
index = ir.IAdd(index, skewBytes(ir, SZ::U32));
}
IR::F32 float_index{};
switch (isberd.mode.Value()) {
case Mode::Patch: float_index = ir.GetPatch(index.Patch());
break;
case Mode::Prim: float_index = ir.GetAttribute(index.Attribute());
break;
case Mode::Attr: float_index = ir.GetAttributeIndexed(index);
break;
default: UNREACHABLE();
}
X(isberd.dest_reg.Value(), ir.BitCast<IR::U32>(float_index));
return;
}
if (isberd.skew.Value()) {
X(isberd.dest_reg.Value(), ir.IAdd(X(isberd.src_reg.Value()), ir.LaneId()));
return;
}
// Fallback copy
X(isberd.dest_reg.Value(), X(isberd.src_reg.Value()));
}
} // namespace Shader::Maxwell
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -36,10 +36,7 @@ enum class ShuffleMode : u64 {
}
}
constexpr u32 QUAD_MASK = (28u << 8) | 3u;
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask,
bool index_is_imm, u32 index_imm, bool mask_is_imm, u32 mask_imm) {
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) {
union {
u64 insn;
BitField<0, 8, IR::Reg> dest_reg;
@@ -48,21 +45,6 @@ void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32
BitField<48, 3, IR::Pred> pred;
} const shfl{insn};
const bool is_quad_candidate{mask_is_imm && mask_imm == QUAD_MASK && index_is_imm &&
v.env.ShaderStage() == Stage::Fragment};
if (is_quad_candidate) {
if (shfl.mode == ShuffleMode::IDX && index_imm <= 3) {
v.X(shfl.dest_reg, v.ir.QuadBroadcast(v.X(shfl.src_reg), v.ir.Imm32(index_imm)));
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
return;
}
if (shfl.mode == ShuffleMode::BFLY && index_imm >= 1 && index_imm <= 3) {
v.X(shfl.dest_reg, v.ir.QuadSwap(v.X(shfl.src_reg), v.ir.Imm32(index_imm - 1)));
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
return;
}
}
const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)};
v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result));
v.X(shfl.dest_reg, result);
@@ -77,14 +59,11 @@ void TranslatorVisitor::SHFL(u64 insn) {
BitField<29, 1, u64> src_b_flag;
BitField<34, 13, u64> src_b_imm;
} const flags{insn};
const bool index_is_imm{flags.src_a_flag != 0};
const bool mask_is_imm{flags.src_b_flag != 0};
const IR::U32 src_a{index_is_imm ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
: GetReg20(insn)};
const IR::U32 src_b{mask_is_imm ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
: GetReg39(insn)};
Shuffle(*this, insn, src_a, src_b, index_is_imm, static_cast<u32>(flags.src_a_imm),
mask_is_imm, static_cast<u32>(flags.src_b_imm));
const IR::U32 src_a{flags.src_a_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
: GetReg20(insn)};
const IR::U32 src_b{flags.src_b_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
: GetReg39(insn)};
Shuffle(*this, insn, src_a, src_b);
}
} // namespace Shader::Maxwell
@@ -498,10 +498,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ShuffleButterfly:
info.uses_subgroup_shuffles = true;
break;
case IR::Opcode::QuadBroadcast:
case IR::Opcode::QuadSwap:
info.uses_quad_shuffles = true;
break;
case IR::Opcode::GetCbufU8:
case IR::Opcode::GetCbufS8:
case IR::Opcode::GetCbufU16:
-2
View File
@@ -37,8 +37,6 @@ struct Profile {
bool support_explicit_workgroup_layout{};
bool support_workgroup_layout_8bit_access{};
bool support_workgroup_layout_16bit_access{};
bool support_shader_quad_control{};
bool support_quad_shuffles{};
bool support_vote{};
u32 supported_subgroup_stages{0x7F};
bool support_viewport_index_layer_non_geometry{};
-1
View File
@@ -252,7 +252,6 @@ struct Info {
bool uses_is_helper_invocation{};
bool uses_subgroup_invocation_id{};
bool uses_subgroup_shuffles{};
bool uses_quad_shuffles{};
std::array<bool, 30> uses_patches{};
std::array<Interpolation, 32> interpolation{};
+1 -3
View File
@@ -33,7 +33,7 @@ add_library(video_core STATIC
control/channel_state_cache.h
control/scheduler.cpp
control/scheduler.h
deferred_destruction_queue.h
delayed_destruction_ring.h
dirty_flags.cpp
dirty_flags.h
dma_pusher.cpp
@@ -158,8 +158,6 @@ add_library(video_core STATIC
renderer_vulkan/vk_compute_pass.h
renderer_vulkan/vk_compute_pipeline.cpp
renderer_vulkan/vk_compute_pipeline.h
renderer_vulkan/vk_descriptor_buffer.cpp
renderer_vulkan/vk_descriptor_buffer.h
renderer_vulkan/vk_descriptor_pool.cpp
renderer_vulkan/vk_descriptor_pool.h
renderer_vulkan/vk_fence_manager.cpp
+88 -283
View File
@@ -7,7 +7,6 @@
#pragma once
#include <algorithm>
#include <bit>
#include <memory>
#include <numeric>
@@ -32,84 +31,44 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
#endif
if (!runtime.CanReportMemoryUsage()) {
memory_budget = FALLBACK_MEMORY_BUDGET;
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
return;
}
memory_budget = runtime.GetDeviceLocalMemory();
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const s64 min_spacing_expected = device_local_memory - 1_GiB;
const s64 min_spacing_critical = device_local_memory - 512_MiB;
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
minimum_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
critical_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
}
template <class P>
BufferCache<P>::~BufferCache() = default;
template <class P>
u64 BufferCache<P>::DeviceUsage(bool force_refresh) {
if (!runtime.CanReportMemoryUsage()) {
return total_used_memory;
}
if (force_refresh || usage_refresh_countdown == 0) {
cached_device_usage = runtime.GetDeviceAllocationUsage();
usage_refresh_countdown = USAGE_REFRESH_INTERVAL;
} else {
--usage_refresh_countdown;
}
return cached_device_usage;
}
template <class P>
u64 BufferCache<P>::ReclaimMemory(u64 target_bytes, bool allow_download) {
if (target_bytes == 0 || in_reclaim) {
return 0;
}
in_reclaim = true;
u64 freed = 0;
const auto clean_up = [&](BufferId buffer_id) {
if (freed >= target_bytes) {
void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
if (num_iterations == 0) {
return true;
}
--num_iterations;
auto& buffer = slot_buffers[buffer_id];
if (!allow_download && IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) {
return false;
}
const u64 buffer_bytes = Common::AlignUp(buffer.SizeBytes(), 1024);
DownloadBufferMemory(buffer);
DeleteBuffer(buffer_id);
freed += buffer_bytes;
return false;
};
const u64 cold_tick =
frame_tick > RECLAIM_GUARD_FRAMES ? frame_tick - RECLAIM_GUARD_FRAMES : 0;
lru_cache.ForEachItemBelow(cold_tick, clean_up);
if (freed == 0) {
lru_cache.ForEachItemBelow(frame_tick > 0 ? frame_tick - 1 : 0, clean_up);
}
in_reclaim = false;
usage_refresh_countdown = 0;
reclaim_stalled = freed == 0;
if (freed > 0) {
reclaim_wait_sync_point = runtime.CurrentSyncPoint();
}
return freed;
}
template <class P>
void BufferCache<P>::EnsureHeadroom(bool allow_download) {
if (reclaim_stalled) {
return;
}
if (runtime.CompletedSyncPoint() < reclaim_wait_sync_point) {
return;
}
const u64 limit = memory_budget > RECLAIM_HEADROOM ? memory_budget - RECLAIM_HEADROOM : 0;
const u64 usage = DeviceUsage(false);
if (usage <= limit) {
return;
}
const u64 target = (limit / 100) * RECLAIM_TARGET_PERCENT;
const u64 excess = usage - target;
const u64 usage_mib = (std::max)(usage >> 20, u64{1});
const u64 share = (((excess >> 20) * (total_used_memory >> 20)) / usage_mib) << 20;
ReclaimMemory((std::min)(share, total_used_memory), allow_download);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
}
template <class P>
@@ -137,11 +96,15 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
usage_refresh_countdown = 0;
reclaim_stalled = false;
EnsureHeadroom(true);
// If we can obtain the memory info, use it instead of the estimate.
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
}
if (total_used_memory >= minimum_memory) {
RunGarbageCollector();
}
++frame_tick;
sentenced_buffers.Reclaim(runtime.CompletedSyncPoint());
delayed_destruction_ring.Tick();
for (auto& buffer : async_buffers_death_ring) {
runtime.FreeDeferredStagingBuffer(buffer);
@@ -212,71 +175,9 @@ std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(DA
template <class P>
void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
if constexpr (!USE_MEMORY_MAPS) {
std::scoped_lock lock{mutex};
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
DownloadBufferMemory(buffer, device_addr, size);
});
return;
}
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 8> downloads;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
std::unique_lock lock{mutex};
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
memory_tracker.ForEachDownloadRangeAndClear(
device_addr, size, [&](u64 device_addr_out, u64 range_size) {
const DAddr buffer_addr = buffer.CpuAddr();
const auto add_download = [&](DAddr start, DAddr end) {
const u64 new_offset = start - buffer_addr;
const u64 new_size = end - start;
downloads.push_back({
BufferCopy{
.src_offset = new_offset,
.dst_offset = total_size_bytes,
.size = new_size,
},
buffer_id,
});
constexpr u64 align = 64ULL;
constexpr u64 mask = ~(align - 1ULL);
total_size_bytes += (new_size + align - 1) & mask;
largest_copy = (std::max)(largest_copy, new_size);
};
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
ClearDownload(device_addr_out, range_size);
gpu_modified_ranges.Subtract(device_addr_out, range_size);
});
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
DownloadBufferMemory(buffer, device_addr, size);
});
if (total_size_bytes == 0) {
return;
}
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
boost::container::small_vector<BufferCopy, 8> writebacks;
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
Buffer& buffer = slot_buffers[buffer_id];
buffer.MarkUsage(copy.src_offset, copy.size);
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
BufferCopy writeback{copy};
writeback.src_offset = static_cast<u64>(buffer.CpuAddr()) + copy.src_offset;
writebacks.push_back(writeback);
}
runtime.PostCopyBarrier();
lock.unlock();
runtime.Finish();
const u8* const base = download_staging.mapped_span.data();
for (const BufferCopy& writeback : writebacks) {
const u64 staging_offset = writeback.dst_offset - download_staging.offset;
device_memory.WriteBlockUnsafe(static_cast<DAddr>(writeback.src_offset),
base + staging_offset, writeback.size);
}
}
template <class P>
@@ -313,7 +214,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
auto& src_buffer = slot_buffers[buffer_a];
auto& dest_buffer = slot_buffers[buffer_b];
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
memory_tracker.UnmarkRegionAsCpuModified(*cpu_dest_address, static_cast<u32>(amount));
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
std::array copies{BufferCopy{
.src_offset = src_buffer.Offset(*cpu_src_address),
.dst_offset = dest_buffer.Offset(*cpu_dest_address),
@@ -772,44 +673,32 @@ void BufferCache<P>::PopAsyncFlushes() {
template <class P>
void BufferCache<P>::PopAsyncBuffers() {
struct Writeback {
DAddr addr;
const u8* src;
u64 size;
};
boost::container::small_vector<Writeback, 8> writebacks;
{
std::scoped_lock lock{mutex};
if (async_buffers.empty()) {
return;
}
if (!async_buffers.front().has_value()) {
async_buffers.pop_front();
return;
}
auto& downloads = pending_downloads.front();
auto& async_buffer = async_buffers.front();
const u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : downloads) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
writebacks.push_back(
{start, &read_mapped_memory[start - device_addr], end - start});
});
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
async_buffers_death_ring.emplace_back(*async_buffer);
if (async_buffers.empty()) {
return;
}
if (!async_buffers.front().has_value()) {
async_buffers.pop_front();
pending_downloads.pop_front();
return;
}
for (const auto& wb : writebacks) {
device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size);
auto& downloads = pending_downloads.front();
auto& async_buffer = async_buffers.front();
u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : downloads) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
end - start);
});
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
}
template <class P>
@@ -920,46 +809,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
if (use_optimized_vertex_buffers) {
auto& flags = maxwell3d->dirty.flags;
const u32 enabled_mask = enabled_vertex_buffers_mask;
bool any_dirty = false;
u32 pending_mask = enabled_mask;
while (pending_mask != 0) {
const u32 index = std::countr_zero(pending_mask);
pending_mask &= (pending_mask - 1);
u32 enabled_mask = enabled_vertex_buffers_mask;
HostBindings<Buffer> bindings{};
u32 last_index = (std::numeric_limits<u32>::max)();
const auto flush_bindings = [&]() {
if (bindings.buffers.empty()) {
return;
}
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
runtime.BindVertexBuffers(bindings);
bindings = HostBindings<Buffer>{};
last_index = (std::numeric_limits<u32>::max)();
};
while (enabled_mask != 0) {
const u32 index = std::countr_zero(enabled_mask);
enabled_mask &= (enabled_mask - 1);
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
any_dirty |= flags[Dirty::VertexBuffer0 + index];
}
if (enabled_mask == 0 || !any_dirty) {
return;
}
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
HostBindings<Buffer> bindings{};
bindings.min_index = min_index;
bindings.max_index = max_index;
for (u32 index = min_index; index < max_index; ++index) {
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
if ((enabled_mask & (1u << index)) == 0) {
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
bindings.offsets.push_back(0);
bindings.sizes.push_back(0);
bindings.strides.push_back(stride);
if (!flags[Dirty::VertexBuffer0 + index]) {
flush_bindings();
continue;
}
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
flush_bindings();
}
if (bindings.buffers.empty()) {
bindings.min_index = index;
}
bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
last_index = index;
}
runtime.BindVertexBuffers(bindings);
flush_bindings();
} else {
HostBindings<typename P::Buffer> host_bindings;
bool any_valid{false};
@@ -1032,6 +921,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
template <class P>
void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind) {
++channel_state->uniform_cache_shots[0];
const Binding& binding = channel_state->uniform_buffers[stage][index];
const DAddr device_addr = binding.device_addr;
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
@@ -1050,12 +940,8 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
return alignment > 1 && (offset % alignment) != 0;
}
}();
const bool cached_buffer_is_current =
has_host_buffer && !memory_tracker.IsRegionCpuModified(device_addr, size);
const bool use_fast_buffer = needs_alignment_stream
|| (has_host_buffer && !cached_buffer_is_current
&& size <= channel_state->uniform_buffer_skip_cache_size
|| (has_host_buffer && size <= channel_state->uniform_buffer_skip_cache_size
&& !memory_tracker.IsRegionGpuModified(device_addr, size));
if (use_fast_buffer) {
if constexpr (IS_OPENGL) {
@@ -1082,7 +968,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
return;
}
++channel_state->uniform_cache_shots[0];
// Classic cached path
if (SynchronizeBuffer(buffer, device_addr, size)) {
++channel_state->uniform_cache_hits[0];
}
@@ -1690,7 +1576,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
template <class P>
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
EnsureHeadroom(false);
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
wanted_size = static_cast<u32>(device_addr_end - device_addr);
@@ -1728,7 +1613,7 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
total_used_memory += Common::AlignUp(size, 1024);
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
} else {
total_used_memory -= std::min<u64>(total_used_memory, Common::AlignUp(size, 1024));
total_used_memory -= Common::AlignUp(size, 1024);
lru_cache.Free(buffer.getLRUID());
}
const DAddr device_addr_begin = buffer.CpuAddr();
@@ -1814,80 +1699,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
}
}
template <class P>
bool BufferCache<P>::TryUnifiedDownloadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] std::span<BufferCopy> copies) {
if constexpr (USE_UNIFIED_MEMORY) {
const u8* const physical_base = device_memory.GetPhysicalBase();
const u64 unified_size = runtime.UnifiedMemorySize();
const u64 window_size = runtime.UnifiedMemoryWindowSize();
if (window_size == 0) {
return false;
}
boost::container::small_vector<u64, 4> window_ids;
boost::container::small_vector<boost::container::small_vector<BufferCopy, 16>, 4> groups;
const auto group_for = [&](u64 window) -> boost::container::small_vector<BufferCopy, 16>& {
for (size_t i = 0; i < window_ids.size(); ++i) {
if (window_ids[i] == window) {
return groups[i];
}
}
window_ids.push_back(window);
groups.emplace_back();
return groups.back();
};
for (const BufferCopy& copy : copies) {
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
u64 downloaded = 0;
while (downloaded < copy.size) {
const DAddr page_addr = device_addr + downloaded;
const u8* const ptr = device_memory.GetPointer<u8>(page_addr);
if (ptr == nullptr) {
return false;
}
const u64 page_offset = page_addr & Core::DEVICE_PAGEMASK;
u64 chunk = (std::min)(copy.size - downloaded,
static_cast<u64>(Core::DEVICE_PAGESIZE) - page_offset);
const u64 phys_offset = static_cast<u64>(ptr - physical_base);
if (phys_offset + chunk > unified_size) {
return false;
}
const u64 window = phys_offset / window_size;
const u64 local_offset = phys_offset % window_size;
chunk = (std::min)(chunk, window_size - local_offset);
auto& group = group_for(window);
if (!group.empty()) {
BufferCopy& last = group.back();
if (last.src_offset + last.size == copy.src_offset + downloaded &&
last.dst_offset + last.size == local_offset) {
last.size += chunk;
downloaded += chunk;
continue;
}
}
group.push_back(BufferCopy{
.src_offset = copy.src_offset + downloaded,
.dst_offset = local_offset,
.size = chunk,
});
downloaded += chunk;
}
}
for (const BufferCopy& copy : copies) {
buffer.MarkUsage(copy.src_offset, copy.size);
}
for (size_t i = 0; i < window_ids.size(); ++i) {
const std::span<BufferCopy> group_span(groups[i].data(), groups[i].size());
runtime.CopyBuffer(runtime.UnifiedMemoryWindowBuffer(window_ids[i]), buffer,
group_span, true);
}
runtime.Finish();
return true;
} else {
return false;
}
}
template <class P>
void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] u64 total_size_bytes,
@@ -1991,12 +1802,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
}
if constexpr (USE_MEMORY_MAPS) {
if constexpr (USE_UNIFIED_MEMORY) {
if (runtime.HasUnifiedMemory() &&
TryUnifiedDownloadMemory(buffer, std::span(copies.data(), copies.size()))) {
return;
}
}
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data();
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
@@ -2067,7 +1872,7 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
#ifdef YUZU_LEGACY
if (!do_not_mark || !immediately_free)
#endif
sentenced_buffers.Push(std::move(slot_buffers[buffer_id]), runtime.CurrentSyncPoint());
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
slot_buffers.erase(buffer_id);
+14 -25
View File
@@ -9,7 +9,6 @@
#include <algorithm>
#include <array>
#include <bit>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
@@ -31,7 +30,7 @@
#include "common/slot_vector.h"
#include "video_core/buffer_cache/buffer_base.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/deferred_destruction_queue.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/kepler_compute.h"
@@ -181,18 +180,15 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
static constexpr bool USE_UNIFIED_MEMORY = P::USE_UNIFIED_MEMORY;
#ifdef YUZU_LEGACY
static constexpr u64 RECLAIM_HEADROOM = 384_MiB;
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
#else
static constexpr u64 RECLAIM_HEADROOM = 512_MiB;
static constexpr s64 TARGET_THRESHOLD = 4_GiB;
#endif
static constexpr u64 FALLBACK_MEMORY_BUDGET = 2_GiB;
static constexpr u32 USAGE_REFRESH_INTERVAL = 16;
static constexpr u64 RECLAIM_GUARD_FRAMES = 8;
static constexpr u64 RECLAIM_TARGET_PERCENT = 95;
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
// Debug Flags.
@@ -219,8 +215,6 @@ public:
void TickFrame();
u64 ReclaimMemory(u64 target_bytes, bool allow_download);
void WriteMemory(DAddr device_addr, u64 size);
void CachedWriteMemory(DAddr device_addr, u64 size);
@@ -364,9 +358,7 @@ private:
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
}
u64 DeviceUsage(bool force_refresh);
void EnsureHeadroom(bool allow_download);
void RunGarbageCollector();
void BindHostIndexBuffer();
@@ -451,8 +443,6 @@ private:
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
bool TryUnifiedDownloadMemory(Buffer& buffer, std::span<BufferCopy> copies);
void DownloadBufferMemory(Buffer& buffer_id);
void DownloadBufferMemory(Buffer& buffer_id, DAddr device_addr, u64 size);
@@ -485,7 +475,12 @@ private:
Tegra::MaxwellDeviceMemoryManager& device_memory;
Common::SlotVector<Buffer> slot_buffers;
DeferredDestructionQueue<Buffer> sentenced_buffers;
#ifdef YUZU_LEGACY
static constexpr size_t TICKS_TO_DESTROY = 6;
#else
static constexpr size_t TICKS_TO_DESTROY = 8;
#endif
DelayedDestructionRing<Buffer, TICKS_TO_DESTROY> delayed_destruction_ring;
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
@@ -520,14 +515,8 @@ private:
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0;
u64 total_used_memory = 0;
u64 memory_budget = 0;
u64 cached_device_usage = 0;
/// Sync point the last reclaim's evictions were queued at. Their memory is not back with the
/// device until this completes, so reclaiming again before then measures stale usage.
u64 reclaim_wait_sync_point = 0;
u32 usage_refresh_countdown = 0;
bool in_reclaim = false;
bool reclaim_stalled = false;
u64 minimum_memory = 0;
u64 critical_memory = 0;
BufferId inline_buffer_id;
#ifdef YUZU_LEGACY
bool immediately_free = false;
+21
View File
@@ -16,6 +16,26 @@
#include "video_core/memory_manager.h"
namespace Tegra::Control {
namespace {
// Match NVK/Nouveau's initial pushbuffer subchannel layout.
constexpr u32 Nvk3DSubchannel = 0;
constexpr u32 NvkComputeSubchannel = 1;
constexpr u32 Nvk2DSubchannel = 3;
constexpr u32 NvkCopySubchannel = 4;
void BindNvkDefaultSubchannels(ChannelState::Payload& payload) {
auto& dma_pusher = payload.dma_pusher;
dma_pusher.BindSubchannel(&payload.maxwell_3d, Nvk3DSubchannel, Engines::EngineTypes::Maxwell3D);
dma_pusher.BindSubchannel(&payload.kepler_compute, NvkComputeSubchannel,
Engines::EngineTypes::KeplerCompute);
// Subchannel 2 is M2MF there; Eden does not expose a 0x9039 engine yet.
dma_pusher.BindSubchannel(&payload.fermi_2d, Nvk2DSubchannel, Engines::EngineTypes::Fermi2D);
dma_pusher.BindSubchannel(&payload.maxwell_dma, NvkCopySubchannel,
Engines::EngineTypes::MaxwellDMA);
}
} // Anonymous namespace
ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state)
: maxwell_3d(memory_manager)
@@ -35,6 +55,7 @@ void ChannelState::Init(Core::System& system, u64 program_id_) {
ASSERT(memory_manager);
program_id = program_id_;
payload.emplace(system, *memory_manager, *this);
BindNvkDefaultSubchannels(*payload);
initialized = true;
}
@@ -1,56 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstddef>
#include <utility>
#include <boost/container/deque.hpp>
#include <boost/container/options.hpp>
#include "common/common_types.h"
namespace VideoCommon {
template <typename T>
class DeferredDestructionQueue {
public:
void Push(T&& object, u64 sync_point) {
entries.emplace_back(std::move(object), sync_point);
}
void Reclaim(u64 completed_sync_point) {
while (!entries.empty() && entries.front().sync_point <= completed_sync_point) {
entries.pop_front();
}
}
void Clear() {
entries.clear();
}
[[nodiscard]] size_t Size() const noexcept {
return entries.size();
}
[[nodiscard]] bool Empty() const noexcept {
return entries.empty();
}
private:
struct Entry {
Entry(T&& object_, u64 sync_point_) noexcept
: object{std::move(object_)}, sync_point{sync_point_} {}
T object;
u64 sync_point;
};
using EntryDequeOptions =
boost::container::deque_options<boost::container::block_size<8u>>::type;
boost::container::deque<Entry, void, EntryDequeOptions> entries;
};
} // namespace VideoCommon
+34
View File
@@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <utility>
#include <vector>
namespace VideoCommon {
/// Container to push objects to be destroyed a few ticks in the future
template <typename T, size_t TICKS_TO_DESTROY>
class DelayedDestructionRing {
public:
void Tick() {
index = (index + 1) % TICKS_TO_DESTROY;
elements[index].clear();
}
void Push(T&& object) {
elements[index].push_back(std::move(object));
}
private:
size_t index = 0;
std::array<std::vector<T>, TICKS_TO_DESTROY> elements;
};
} // namespace VideoCommon
+1 -2
View File
@@ -71,8 +71,7 @@ void Fermi2D::Blit() {
constexpr s64 null_derivative = 1ULL << 32;
Surface src = regs.src;
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const u64 src_area = static_cast<u64>(src.width) * static_cast<u64>(src.height);
const bool delegate_to_gpu = src_area > 512ULL * 512ULL && bytes_per_pixel <= 8 &&
const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
src.format != regs.dst.format;
auto srcX = args.src_x0;
+14 -3
View File
@@ -22,10 +22,21 @@
namespace Tegra::Engines {
namespace {
constexpr u32 Gf100BindClassMask = 0xffff;
constexpr u32 Gf100BindValidMask = 0x1f0000 | Gf100BindClassMask;
} // Anonymous namespace
void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
// Bind the current subchannel to the desired engine id.
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument);
const auto engine_id = static_cast<EngineID>(method_call.argument);
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {:#x}", method_call.subchannel, method_call.argument);
u32 engine = method_call.argument;
if ((engine & ~Gf100BindClassMask) != 0 && (engine & ~Gf100BindValidMask) == 0) {
engine &= Gf100BindClassMask;
}
const auto engine_id = static_cast<EngineID>(engine);
bound_engines[method_call.subchannel] = engine_id;
switch (engine_id) {
case EngineID::FERMI_TWOD_A:
+10 -10
View File
@@ -18,7 +18,7 @@
#include "common/common_types.h"
#include "common/settings.h"
#include "common/thread.h"
#include "video_core/deferred_destruction_queue.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/syncpoint_manager.h"
@@ -50,8 +50,7 @@ public:
/// Notify the fence manager about a new frame
void TickFrame() {
std::unique_lock lock(ring_guard);
++retire_tick;
sentenced_fences.Reclaim(retire_tick > RETIRE_DELAY ? retire_tick - RETIRE_DELAY : 0);
delayed_destruction_ring.Tick();
}
// Unlike other fences, this one doesn't
@@ -92,6 +91,9 @@ public:
func();
}
fences.push(std::move(new_fence));
if (should_flush) {
rasterizer.FlushCommands();
}
if constexpr (can_async_check) {
guard.unlock();
cv.notify_all();
@@ -184,7 +186,7 @@ private:
}
{
std::unique_lock lock(ring_guard);
sentenced_fences.Push(std::move(current_fence), retire_tick);
delayed_destruction_ring.Push(std::move(current_fence));
}
fences.pop();
}
@@ -217,7 +219,7 @@ private:
}
{
std::unique_lock lock(ring_guard);
sentenced_fences.Push(std::move(current_fence), retire_tick);
delayed_destruction_ring.Push(std::move(current_fence));
}
}
}
@@ -236,10 +238,10 @@ private:
void PopAsyncFlushes() {
{
std::scoped_lock lock{texture_cache.mutex};
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
texture_cache.PopAsyncFlushes();
buffer_cache.PopAsyncFlushes();
}
buffer_cache.PopAsyncFlushes();
query_cache.PopAsyncFlushes();
}
@@ -262,9 +264,7 @@ private:
std::jthread fence_thread;
static constexpr u64 RETIRE_DELAY = 8;
u64 retire_tick = 1;
DeferredDestructionQueue<TFence> sentenced_fences;
DelayedDestructionRing<TFence, 8> delayed_destruction_ring;
};
} // namespace VideoCommon
-1
View File
@@ -30,7 +30,6 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
thread = std::jthread([&](std::stop_token stop_token) {
Common::SetCurrentThreadName("GPU");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
Common::SetCurrentThreadToPerformanceCores();
system.RegisterHostThread();
auto current_context = context.Acquire();
@@ -17,13 +17,11 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
@@ -34,7 +32,6 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
+16 -10
View File
@@ -77,8 +77,14 @@ uvec4 local_buff;
uvec4 color_endpoint_data;
int color_bitsread = 0;
#define MAX_WEIGHT_VALUES 64
uint result_vector[MAX_WEIGHT_VALUES];
// Global "vector" to be pushed into when decoding
// At most will require BLOCK_WIDTH x BLOCK_HEIGHT in single plane mode
// At most will require BLOCK_WIDTH x BLOCK_HEIGHT x 2 in dual plane mode
// So the maximum would be 144 (12 x 12) elements, x 2 for two planes
#define DIVCEIL(number, divisor) (number + divisor - 1) / divisor
#define ARRAY_NUM_ELEMENTS 144
#define VECTOR_ARRAY_SIZE DIVCEIL(ARRAY_NUM_ELEMENTS * 2, 4)
uint result_vector[ARRAY_NUM_ELEMENTS * 2];
int result_index = 0;
uint result_vector_max_index;
@@ -486,7 +492,7 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits, o
A = ReplicateBitTo9((bitval & 1));
switch (encoding) {
case JUST_BITS:
color_values[out_index++] = FastReplicateTo8(bitval, bitlen);
color_values[++out_index] = FastReplicateTo8(bitval, bitlen);
break;
case TRIT: {
D = QuintTritValue(val);
@@ -565,7 +571,7 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits, o
uint T = (D * C) + B;
T ^= A;
T = (A & 0x80) | (T >> 2);
color_values[out_index++] = T;
color_values[++out_index] = T;
}
}
}
@@ -747,12 +753,12 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
#define READ_UINT_VALUES(N) \
uvec4 V[2]; \
for (uint i = 0; i < N; i++) { \
V[i / 4][i % 4] = color_values[colvals_index++]; \
V[i / 4][i % 4] = color_values[++colvals_index]; \
}
#define READ_INT_VALUES(N) \
ivec4 V[2]; \
for (uint i = 0; i < N; i++) { \
V[i / 4][i % 4] = int(color_values[colvals_index++]); \
V[i / 4][i % 4] = int(color_values[++colvals_index]); \
}
switch (color_endpoint_mode) {
@@ -1219,10 +1225,6 @@ void DecompressBlock(ivec3 coord) {
FillError(coord);
return;
}
if (GetNumWeightValues(size_params, dual_plane) > MAX_WEIGHT_VALUES) {
FillError(coord);
return;
}
uint partition_index = 1;
uvec4 color_endpoint_mode = uvec4(0);
uint ced_pointer = 0;
@@ -1382,7 +1384,11 @@ void DecompressBlock(ivec3 coord) {
p = Cf / 65535.0f;
}
#ifdef VULKAN
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
#else
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
#endif
}
}
}
@@ -1,104 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#extension GL_EXT_shader_16bit_storage : require
#extension GL_EXT_shader_8bit_storage : require
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint layer_stride;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += pos.z * pc.layer_stride;
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint slice_size;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
uint block_depth;
uint block_depth_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += (pos.z >> pc.block_depth) * pc.slice_size;
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -1,19 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2D img_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(img_in, coord, 0).r;
}
+93 -193
View File
@@ -58,9 +58,8 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, GPU
MemoryManager::~MemoryManager() = default;
template <bool is_big_page>
MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const {
if constexpr (is_big_page) {
MemoryManager::EntryType MemoryManager::GetEntry(size_t position, bool is_big_page) const {
if (is_big_page) {
position = position >> big_page_bits;
const u64 entry_mask = big_entries[position / 32];
const size_t sub_index = position % 32;
@@ -73,9 +72,8 @@ MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const {
}
}
template <bool is_big_page>
void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
if constexpr (is_big_page) {
void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry, bool is_big_page) {
if (is_big_page) {
position = position >> big_page_bits;
const u64 entry_mask = big_entries[position / 32];
const size_t sub_index = position % 32;
@@ -108,23 +106,21 @@ inline void MemoryManager::SetBigPageContinuous(size_t big_page_index, bool valu
(~(1ULL << sub_index) & continuous_mask) | (value ? 1ULL << sub_index : 0);
}
template <MemoryManager::EntryType entry_type>
GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
PTEKind kind) {
GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, MemoryManager::EntryType entry_type) {
[[maybe_unused]] u64 remaining_size{size};
if constexpr (entry_type == EntryType::Mapped) {
if (entry_type == EntryType::Mapped) {
page_table.ReserveRange(gpu_addr, size);
}
for (u64 offset{}; offset < size; offset += page_size) {
const GPUVAddr current_gpu_addr = gpu_addr + offset;
[[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
SetEntry<false>(current_gpu_addr, entry_type);
[[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr, false);
SetEntry(current_gpu_addr, entry_type, false);
if (current_entry_type != entry_type) {
rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, page_size);
}
if constexpr (entry_type == EntryType::Mapped) {
if (entry_type == EntryType::Mapped) {
const DAddr current_dev_addr = dev_addr + offset;
const auto index = PageEntryIndex<false>(current_gpu_addr);
const auto index = PageEntryIndex(current_gpu_addr, false);
const u32 sub_value = static_cast<u32>(current_dev_addr >> cpu_page_bits);
page_table[index] = sub_value;
}
@@ -134,20 +130,18 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr de
return gpu_addr;
}
template <MemoryManager::EntryType entry_type>
GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr,
size_t size, PTEKind kind) {
GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, MemoryManager::EntryType entry_type) {
[[maybe_unused]] u64 remaining_size{size};
for (u64 offset{}; offset < size; offset += big_page_size) {
const GPUVAddr current_gpu_addr = gpu_addr + offset;
[[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
SetEntry<true>(current_gpu_addr, entry_type);
[[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr, true);
SetEntry(current_gpu_addr, entry_type, true);
if (current_entry_type != entry_type) {
rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, big_page_size);
}
if constexpr (entry_type == EntryType::Mapped) {
if (entry_type == EntryType::Mapped) {
const DAddr current_dev_addr = dev_addr + offset;
const auto index = PageEntryIndex<true>(current_gpu_addr);
const auto index = PageEntryIndex(current_gpu_addr, true);
const u32 sub_value = static_cast<u32>(current_dev_addr >> cpu_page_bits);
big_page_table_dev[index] = sub_value;
const bool is_continuous = ([&] {
@@ -181,19 +175,16 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
rasterizer = rasterizer_;
}
GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind,
bool is_big_pages) {
if (is_big_pages) [[likely]] {
return BigPageTableOp<EntryType::Mapped>(gpu_addr, dev_addr, size, kind);
}
return PageTableOp<EntryType::Mapped>(gpu_addr, dev_addr, size, kind);
GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind, bool is_big_pages) {
if (is_big_pages)
return BigPageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
return PageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
}
GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) {
if (is_big_pages) [[likely]] {
return BigPageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
}
return PageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
if (is_big_pages)
return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
}
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
@@ -207,26 +198,21 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
}
page_stash.clear();
BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Free);
PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Free);
}
std::optional<DAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] {
return std::nullopt;
}
if (GetEntry<true>(gpu_addr) != EntryType::Mapped) [[unlikely]] {
if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
if (GetEntry(gpu_addr, true) != EntryType::Mapped) [[unlikely]] {
if (GetEntry(gpu_addr, false) != EntryType::Mapped)
return std::nullopt;
}
const DAddr dev_addr_base = static_cast<DAddr>(page_table[PageEntryIndex<false>(gpu_addr)])
<< cpu_page_bits;
const DAddr dev_addr_base = DAddr(page_table[PageEntryIndex(gpu_addr, false)]) << cpu_page_bits;
return dev_addr_base + (gpu_addr & page_mask);
}
const DAddr dev_addr_base =
static_cast<DAddr>(big_page_table_dev[PageEntryIndex<true>(gpu_addr)]) << cpu_page_bits;
const DAddr dev_addr_base = DAddr(big_page_table_dev[PageEntryIndex(gpu_addr, true)]) << cpu_page_bits;
return dev_addr_base + (gpu_addr & big_page_mask);
}
@@ -299,10 +285,8 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
#pragma inline_recursion(on)
#endif
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
FuncMapped&& func_mapped, FuncReserved&& func_reserved,
FuncUnmapped&& func_unmapped) const {
template <typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, bool is_big_page, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const {
using FuncMappedReturn =
typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type;
using FuncReservedReturn =
@@ -315,7 +299,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
u64 used_page_size;
u64 used_page_mask;
u64 used_page_bits;
if constexpr (is_big_pages) {
if (is_big_page) {
used_page_size = big_page_size;
used_page_mask = big_page_mask;
used_page_bits = big_page_bits;
@@ -332,7 +316,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
while (remaining_size > 0) {
const std::size_t copy_amount{
(std::min)(static_cast<std::size_t>(used_page_size) - page_offset, remaining_size)};
auto entry = GetEntry<is_big_pages>(current_address);
auto entry = GetEntry(current_address, is_big_page);
if (entry == EntryType::Mapped) [[likely]] {
if constexpr (BOOL_BREAK_MAPPED) {
if (func_mapped(page_index, page_offset, copy_amount)) {
@@ -367,164 +351,91 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
}
}
template <bool is_safe>
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
[[maybe_unused]] VideoCommon::CacheType which) const {
const u8* run_src{nullptr};
u8* run_dst{nullptr};
std::size_t run_size{0};
auto flush_run = [&] {
if (run_size == 0) {
return;
}
std::memcpy(run_dst, run_src, run_size);
run_src = nullptr;
run_dst = nullptr;
run_size = 0;
};
auto append_run = [&](const u8* physical, std::size_t copy_amount) {
if (physical == nullptr) [[unlikely]] {
flush_run();
std::memset(dest_buffer, 0, copy_amount);
return;
}
if (run_size != 0 && run_src + run_size == physical &&
run_dst + run_size == static_cast<u8*>(dest_buffer)) {
run_size += copy_amount;
return;
}
flush_run();
run_src = physical;
run_dst = static_cast<u8*>(dest_buffer);
run_size = copy_amount;
};
auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
flush_run();
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, [[maybe_unused]] VideoCommon::CacheType which, bool unsafe) const {
auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
std::memset(dest_buffer, 0, copy_amount);
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
};
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) {
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
if (!unsafe) {
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
}
append_run(memory.GetPointer<u8>(dev_addr_base), copy_amount);
u8* physical = memory.GetPointer<u8>(dev_addr_base);
std::memcpy(dest_buffer, physical, copy_amount);
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
};
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) {
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
if (!unsafe) {
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
}
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
flush_run();
memory.ReadBlockUnsafe(dev_addr_base, dest_buffer, copy_amount);
} else {
append_run(memory.GetPointer<u8>(dev_addr_base), copy_amount);
u8* physical = memory.GetPointer<u8>(dev_addr_base);
std::memcpy(dest_buffer, physical, copy_amount);
}
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
};
auto read_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
auto read_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, mapped_normal, set_to_zero, set_to_zero);
MemoryOperation(base, copy_amount, false, mapped_normal, set_to_zero, set_to_zero);
};
MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
flush_run();
MemoryOperation(gpu_src_addr, size, true, mapped_big, set_to_zero, read_short_pages);
}
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which) const {
ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which) const {
ReadBlockImpl(gpu_src_addr, dest_buffer, size, which, false);
}
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
const std::size_t size) const {
ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const {
ReadBlockImpl(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None, true);
}
template <bool is_safe>
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
[[maybe_unused]] VideoCommon::CacheType which) {
const u8* run_src{nullptr};
u8* run_dst{nullptr};
std::size_t run_size{0};
auto flush_run = [&] {
if (run_size == 0) {
return;
}
std::memcpy(run_dst, run_src, run_size);
run_src = nullptr;
run_dst = nullptr;
run_size = 0;
};
auto append_run = [&](u8* physical, std::size_t copy_amount) {
if (physical == nullptr) [[unlikely]] {
flush_run();
return;
}
if (run_size != 0 && run_dst + run_size == physical &&
run_src + run_size == static_cast<const u8*>(src_buffer)) {
run_size += copy_amount;
return;
}
flush_run();
run_src = static_cast<const u8*>(src_buffer);
run_dst = physical;
run_size = copy_amount;
};
auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
flush_run();
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, [[maybe_unused]] VideoCommon::CacheType which, bool unsafe) {
auto just_advance = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
};
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) {
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
if (!unsafe) {
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
}
append_run(memory.GetPointer<u8>(dev_addr_base), copy_amount);
u8* physical = memory.GetPointer<u8>(dev_addr_base);
std::memcpy(physical, src_buffer, copy_amount);
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
};
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
if constexpr (is_safe) {
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
if (!unsafe) {
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
}
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
flush_run();
memory.WriteBlockUnsafe(dev_addr_base, src_buffer, copy_amount);
} else {
append_run(memory.GetPointer<u8>(dev_addr_base), copy_amount);
u8* physical = memory.GetPointer<u8>(dev_addr_base);
std::memcpy(physical, src_buffer, copy_amount);
}
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
};
auto write_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
auto write_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, mapped_normal, just_advance, just_advance);
MemoryOperation(base, copy_amount, false, mapped_normal, just_advance, just_advance);
};
MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
flush_run();
MemoryOperation(gpu_dest_addr, size, true, mapped_big, just_advance, write_short_pages);
}
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
VideoCommon::CacheType which) {
WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which);
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which) {
WriteBlockImpl(gpu_dest_addr, src_buffer, size, which, false);
}
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
std::size_t size) {
WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
WriteBlockImpl(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None, true);
}
void MemoryManager::WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
WriteBlockImpl(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None, true);
accumulator.Add(gpu_dest_addr, size);
}
@@ -535,21 +446,18 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size,
[[maybe_unused]] std::size_t copy_amount) {};
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
};
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
};
auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
};
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, flush_short_pages);
}
bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
@@ -574,10 +482,10 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
return result;
};
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, check_short_pages);
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, check_short_pages);
return result;
}
@@ -614,10 +522,10 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
MemoryOperation(base, copy_amount, false, short_check, fail, fail);
return result;
};
MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
MemoryOperation(gpu_addr, size, true, big_check, fail, check_short_pages);
return range_so_far;
}
@@ -633,21 +541,18 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
[[maybe_unused]] std::size_t copy_amount) {};
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
};
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
const DAddr dev_addr_base =
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
};
auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
};
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, invalidate_short_pages);
}
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
@@ -659,16 +564,16 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std
}
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
if (GetEntry(gpu_addr, true) == EntryType::Mapped) [[likely]] {
size_t page_index = gpu_addr >> big_page_bits;
if (IsBigPageContinuous(page_index)) [[likely]] {
const std::size_t page{(gpu_addr & big_page_mask) + size};
const std::size_t page{(page_index & big_page_mask) + size};
return page <= big_page_size;
}
const std::size_t page{(gpu_addr & Core::DEVICE_PAGEMASK) + size};
return page <= Core::DEVICE_PAGESIZE;
}
if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
if (GetEntry(gpu_addr, false) != EntryType::Mapped) {
return false;
}
const std::size_t page{(gpu_addr & Core::DEVICE_PAGEMASK) + size};
@@ -706,10 +611,10 @@ bool MemoryManager::IsContinuousRange(GPUVAddr gpu_addr, std::size_t size) const
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
MemoryOperation(base, copy_amount, false, short_check, fail, fail);
return !result;
};
MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
MemoryOperation(gpu_addr, size, true, big_check, fail, check_short_pages);
return result;
}
@@ -722,13 +627,12 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
};
auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
[[maybe_unused]] std::size_t copy_amount) { return false; };
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
std::size_t copy_amount) {
auto check_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, pass, pass, fail);
MemoryOperation(base, copy_amount, false, pass, pass, fail);
return !result;
};
MemoryOperation<true>(gpu_addr, size, pass, fail, check_short_pages);
MemoryOperation(gpu_addr, size, true, pass, fail, check_short_pages);
return result;
}
@@ -740,13 +644,9 @@ MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const {
}
template <bool is_gpu_address>
void MemoryManager::GetSubmappedRangeImpl(
GPUVAddr gpu_addr, std::size_t size,
boost::container::small_vector<
std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result)
void MemoryManager::GetSubmappedRangeImpl(GPUVAddr gpu_addr, std::size_t size, boost::container::small_vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result)
const {
std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>>
last_segment{};
std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>> last_segment{};
std::optional<DAddr> old_page_addr{};
const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
[[maybe_unused]] std::size_t offset,
@@ -802,9 +702,9 @@ void MemoryManager::GetSubmappedRangeImpl(
};
auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
GPUVAddr base = (page_index << big_page_bits) + offset;
MemoryOperation<false>(base, copy_amount, extend_size_short, split, split);
MemoryOperation(base, copy_amount, false, extend_size_short, split, split);
};
MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
MemoryOperation(gpu_addr, size, true, extend_size_big, split, do_short_pages);
split(0, 0, 0);
}
+19 -40
View File
@@ -45,7 +45,7 @@ public:
static constexpr bool HAS_FLUSH_INVALIDATION = true;
size_t GetID() const {
inline size_t GetID() const noexcept {
return unique_identifier;
}
@@ -66,16 +66,15 @@ public:
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
template <typename T>
[[nodiscard]] T* GetPointer(GPUVAddr addr) {
const auto address{GpuToCpuAddress(addr)};
if (!address) {
[[nodiscard]] inline T* GetPointer(GPUVAddr addr) noexcept {
const auto address = GpuToCpuAddress(addr);
if (!address)
return {};
}
return memory.GetPointer<T>(*address);
}
template <typename T>
[[nodiscard]] const T* GetPointer(GPUVAddr addr) const {
[[nodiscard]] inline const T* GetPointer(GPUVAddr addr) const noexcept {
return GetPointer<T*>(addr);
}
@@ -85,12 +84,9 @@ public:
* in the Host Memory counterpart. Note: This functions cause Host GPU Memory
* Flushes and Invalidations, respectively to each operation.
*/
void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All);
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
VideoCommon::CacheType which = VideoCommon::CacheType::All);
void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All);
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All);
/**
* ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@@ -160,21 +156,14 @@ public:
u8* GetSpan(const GPUVAddr src_addr, const std::size_t size);
private:
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
template <typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, bool is_big_page, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
template <bool is_safe>
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which) const;
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which, bool unsafe) const;
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which, bool unsafe);
template <bool is_safe>
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
VideoCommon::CacheType which);
template <bool is_big_page>
[[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
if constexpr (is_big_page) {
[[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr, bool is_big_page) const {
if (is_big_page) {
return (gpu_addr >> big_page_bits) & big_page_table_mask;
} else {
return (gpu_addr >> page_bits) & page_table_mask;
@@ -187,9 +176,7 @@ private:
template <bool is_gpu_address>
void GetSubmappedRangeImpl(
GPUVAddr gpu_addr, std::size_t size,
boost::container::small_vector<
std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>&
result) const;
boost::container::small_vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result) const;
Core::System& system;
MaxwellDeviceMemoryManager& memory;
@@ -219,19 +206,11 @@ private:
std::vector<u64> entries;
std::vector<u64> big_entries;
template <EntryType entry_type>
GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
PTEKind kind);
GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, EntryType entry_type);
GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, EntryType entry_type);
template <EntryType entry_type>
GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
PTEKind kind);
template <bool is_big_page>
inline EntryType GetEntry(size_t position) const;
template <bool is_big_page>
inline void SetEntry(size_t position, EntryType entry);
inline EntryType GetEntry(size_t position, bool is_big_page) const;
inline void SetEntry(size_t position, EntryType entry, bool is_big_page);
Common::MultiLevelPageTable<u32> page_table;
Common::RangeMap<GPUVAddr, PTEKind> kind_map;
@@ -93,17 +93,7 @@ public:
void PostCopyBarrier();
void Finish();
void TickFrame(Common::SlotVector<Buffer>&) noexcept {
++sync_point;
}
u64 CurrentSyncPoint() const noexcept {
return sync_point;
}
u64 CompletedSyncPoint() const noexcept {
return sync_point > SYNC_POINT_DELAY ? sync_point - SYNC_POINT_DELAY : 0;
}
void TickFrame(Common::SlotVector<Buffer>&) noexcept {}
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
@@ -138,10 +128,6 @@ public:
u64 GetDeviceMemoryUsage() const;
u64 GetDeviceAllocationUsage() const {
return GetDeviceMemoryUsage();
}
void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
const GLuint handle = fast_uniforms[stage][binding_index].handle;
const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
@@ -227,13 +213,9 @@ private:
GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV,
};
static constexpr u64 SYNC_POINT_DELAY = 8;
const Device& device;
StagingBufferPool& staging_buffer_pool;
u64 sync_point = 1;
bool has_fast_buffer_sub_data = false;
bool use_assembly_shaders = false;
bool has_unified_vertex_buffers = false;
@@ -279,7 +261,6 @@ struct BufferCacheParams {
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
static constexpr bool USE_UNIFIED_MEMORY = false;
};
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
@@ -485,6 +485,7 @@ void RasterizerOpenGL::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
texture_cache.DownloadMemory(addr, size);
}
if ((True(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.DownloadMemory(addr, size);
}
if ((True(which & VideoCommon::CacheType::QueryCache))) {
@@ -87,10 +87,6 @@ public:
u64 GetDeviceMemoryUsage() const;
u64 GetDeviceAllocationUsage() const {
return GetDeviceMemoryUsage();
}
bool CanReportMemoryUsage() const {
return device.CanReportMemoryUsage();
}
@@ -143,19 +139,7 @@ public:
bool HasNativeASTC() const noexcept;
void TickFrame() {
++sync_point;
}
u64 CurrentSyncPoint() const noexcept {
return sync_point;
}
u64 CompletedSyncPoint() const noexcept {
return sync_point > SYNC_POINT_DELAY ? sync_point - SYNC_POINT_DELAY : 0;
}
void WaitSyncPoint(u64) {}
void TickFrame() {}
StateTracker& GetStateTracker() {
return state_tracker;
@@ -190,9 +174,6 @@ private:
std::array<OGLFramebuffer, 4> rescale_read_fbos;
const Settings::ResolutionScalingInfo& resolution;
u64 device_access_memory;
static constexpr u64 SYNC_POINT_DELAY = 8;
u64 sync_point = 1;
};
class Image : public VideoCommon::ImageBase {
@@ -389,7 +370,6 @@ struct TextureCacheParams {
static constexpr bool HAS_EMULATED_COPIES = true;
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool HAS_TIMELINE_SYNC_POINTS = false;
using Runtime = OpenGL::TextureCacheRuntime;
using Image = OpenGL::Image;
+94 -240
View File
@@ -21,7 +21,6 @@
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
@@ -520,8 +519,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
}
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
VkFormat format, u32 base_level,
VkImageAspectFlags aspect_mask) {
VkFormat format, u32 base_level) {
return device.CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
@@ -536,7 +534,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange{
.aspectMask = aspect_mask,
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = base_level,
.levelCount = 1,
.baseArrayLayer = 0,
@@ -547,10 +545,6 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
const VkRenderPass render_pass = framebuffer->RenderPass();
if (!render_pass) {
framebuffer->BeginRendering(cmdbuf);
return;
}
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
const VkExtent2D render_area = framebuffer->RenderArea();
const VkRenderPassBeginInfo renderpass_bi{
@@ -567,31 +561,6 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer)
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
}
void EndRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
if (framebuffer->RenderPass()) {
cmdbuf.EndRenderPass();
} else {
cmdbuf.EndRendering();
}
}
[[nodiscard]] VkPipelineRenderingCreateInfo MakePipelineRenderingCreateInfo(
const Framebuffer* framebuffer) {
return VkPipelineRenderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.pNext = nullptr,
.viewMask = 0,
.colorAttachmentCount = framebuffer->NumColorAttachments(),
.pColorAttachmentFormats = framebuffer->ColorAttachmentFormats().data(),
.depthAttachmentFormat = framebuffer->HasAspectDepthBit()
? framebuffer->DepthAttachmentFormat()
: VK_FORMAT_UNDEFINED,
.stencilAttachmentFormat = framebuffer->HasAspectStencilBit()
? framebuffer->DepthAttachmentFormat()
: VK_FORMAT_UNDEFINED,
};
}
} // Anonymous namespace
BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
@@ -641,8 +610,6 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -656,12 +623,10 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
RecordShaderReadBarrier(scheduler, src_image_view);
@@ -686,11 +651,9 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
@@ -703,7 +666,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
cmdbuf.Draw(3, 1, 0, 0);
EndRenderPass(cmdbuf, dst_framebuffer);
cmdbuf.EndRenderPass();
});
}
@@ -713,12 +676,10 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
const BlitMSAAPipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.samples = dst_framebuffer->Samples(),
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
RecordShaderReadBarrier(scheduler, src_image_view);
@@ -742,7 +703,7 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
const bool resolve_stencil =
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
const VkPipeline pipeline =
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer, resolve_stencil);
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
const VkPipelineLayout layout =
resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
@@ -786,12 +747,10 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view = src_image_view.StencilView();
@@ -813,25 +772,25 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer);
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer);
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer);
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer);
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
}
@@ -842,35 +801,35 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping");
return;
}
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer,
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
convert_abgr8_to_d24s8_frag);
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer,
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(),
convert_abgr8_to_d32f_frag);
Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer,
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
convert_d32f_to_abgr8_frag);
ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer,
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
convert_d24s8_to_abgr8_frag);
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer,
ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
convert_s8d24_to_abgr8_frag);
ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
@@ -881,10 +840,8 @@ void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_ma
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record(
@@ -910,10 +867,8 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
.stencil_mask = stencil_mask,
.stencil_compare_mask = stencil_compare_mask,
.stencil_ref = stencil_ref,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key, dst_framebuffer);
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) {
@@ -940,34 +895,16 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
const s32 scale_y = 1 << samples_y;
const VkSampleCountFlagBits samples =
msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
const auto dst_surface_type = VideoCore::Surface::GetFormatType(dst_format);
const bool is_depth = dst_surface_type == VideoCore::Surface::SurfaceType::Depth ||
dst_surface_type == VideoCore::Surface::SurfaceType::DepthStencil;
const bool has_stencil = dst_surface_type == VideoCore::Surface::SurfaceType::DepthStencil;
const VkImageAspectFlags view_aspect =
is_depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
VkImageAspectFlags barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (is_depth) {
barrier_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
if (has_stencil) {
barrier_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
}
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
if (is_depth) {
renderpass_key.depth_format = dst_format;
} else {
renderpass_key.color_formats[0] = dst_format;
renderpass_key.depth_format = VideoCore::Surface::PixelFormat::Invalid;
}
renderpass_key.color_formats[0] = dst_format;
renderpass_key.depth_format = VideoCore::Surface::PixelFormat::Invalid;
renderpass_key.samples = samples;
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
const MSAACopyPipelineKey key{
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = msaa_to_non_msaa,
.is_depth = is_depth,
};
const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
@@ -983,10 +920,10 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level), view_aspect);
static_cast<u32>(copy.src_subresource.base_level));
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level), view_aspect);
static_cast<u32>(copy.dst_subresource.base_level));
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
@@ -1012,64 +949,50 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, pipeline, layout, sampler, renderpass,
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
src = src_image, dst = dst_image, render_area, is_depth, barrier_aspect,
src = src_image, dst = dst_image, render_area,
push_constants](vk::CommandBuffer cmdbuf) {
const VkImageSubresourceRange src_range{
.aspectMask = barrier_aspect,
constexpr VkImageSubresourceRange color_range{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const VkImageSubresourceRange dst_range = src_range;
const VkAccessFlags attachment_read =
is_depth ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
: VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
const VkAccessFlags attachment_write =
is_depth ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
: VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
const VkPipelineStageFlags depth_stage =
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
const VkPipelineStageFlags attachment_stage =
is_depth ? depth_stage
: static_cast<VkPipelineStageFlags>(
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = attachment_write | VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src,
.subresourceRange = src_range,
.subresourceRange = color_range,
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = attachment_write | VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = attachment_read | attachment_write,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = dst_range,
.subresourceRange = color_range,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | attachment_stage,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, nullptr, nullptr, pre_barriers);
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
@@ -1102,16 +1025,16 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = attachment_write,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = dst_range,
.subresourceRange = color_range,
};
cmdbuf.PipelineBarrier(attachment_stage,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
@@ -1217,14 +1140,12 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
scheduler.InvalidateState();
}
VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) {
const auto it = std::ranges::find(blit_color_keys, key);
if (it != blit_color_keys.end()) {
return *blit_color_pipelines[std::distance(blit_color_keys.begin(), it)];
}
blit_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_color_to_color_frag);
const VkPipelineColorBlendAttachmentState blend_attachment{
@@ -1252,7 +1173,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1270,23 +1191,21 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *blit_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key) {
const auto it = std::ranges::find(blit_depth_stencil_keys, key);
if (it != blit_depth_stencil_keys.end()) {
return *blit_depth_stencil_pipelines[std::distance(blit_depth_stencil_keys.begin(), it)];
}
blit_depth_stencil_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1304,50 +1223,42 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *blit_depth_stencil_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
const auto it = std::ranges::find(clear_color_keys, key);
if (it != clear_color_keys.end()) {
return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
}
clear_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
const u32 num_color = framebuffer->NumColorAttachments();
constexpr VkColorComponentFlags full_write_mask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
std::array<VkPipelineColorBlendAttachmentState, VideoCommon::NUM_RT> blend_attachments{};
for (u32 index = 0; index < num_color; ++index) {
blend_attachments[index] = VkPipelineColorBlendAttachmentState{
.blendEnable = index == 0 ? VK_TRUE : VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = index == 0 ? full_write_mask : VkColorComponentFlags{0},
};
}
const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_CLEAR,
.attachmentCount = num_color,
.pAttachments = blend_attachments.data(),
.attachmentCount = 1,
.pAttachments = &color_blend_attachment_state,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1365,31 +1276,18 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *clear_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer) {
const BlitDepthStencilPipelineKey& key) {
const auto it = std::ranges::find(clear_stencil_keys, key);
if (it != clear_stencil_keys.end()) {
return *clear_stencil_pipelines[std::distance(clear_stencil_keys.begin(), it)];
}
clear_stencil_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag);
const u32 num_color = framebuffer->NumColorAttachments();
std::array<VkPipelineColorBlendAttachmentState, VideoCommon::NUM_RT> blend_attachments{};
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_CLEAR,
.attachmentCount = num_color,
.pAttachments = blend_attachments.data(),
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const auto stencil = VkStencilOpState{
.failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_REPLACE,
@@ -1416,7 +1314,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1427,25 +1325,23 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &color_blend_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *clear_color_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *clear_stencil_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key,
const Framebuffer* framebuffer) {
VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key) {
const auto it = std::ranges::find(blit_msaa_color_keys, key);
if (it != blit_msaa_color_keys.end()) {
return *blit_msaa_color_pipelines[std::distance(blit_msaa_color_keys.begin(), it)];
}
blit_msaa_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_color_msaa_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
@@ -1461,7 +1357,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_msaa_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1479,32 +1375,26 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *blit_msaa_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(
const Framebuffer* framebuffer, bool resolve_stencil) {
const VkRenderPass renderpass = framebuffer->RenderPass();
const ResolveDepthStencilPipelineKey key{
.renderpass = renderpass,
.depth_format = framebuffer->DepthAttachmentFormat(),
};
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil) {
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
auto& pipelines = resolve_stencil ? resolve_depth_stencil_pipelines : resolve_depth_pipelines;
const auto it = std::ranges::find(keys, key);
const auto it = std::ranges::find(keys, renderpass);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
keys.push_back(renderpass);
const std::array stages =
MakeStages(*full_screen_vert,
resolve_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1523,7 +1413,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *pipelines.back();
}
@@ -1533,36 +1423,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
}
msaa_copy_keys.push_back(key);
const VkShaderModule frag_module =
key.msaa_to_non_msaa
? *convert_msaa_to_non_msaa_frag
: (key.is_depth ? *convert_non_msaa_to_msaa_depth_frag
: *convert_non_msaa_to_msaa_frag);
const std::array stages = MakeStages(*clear_color_vert, frag_module);
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = VK_FALSE,
.front = {},
.back = {},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
static constexpr VkPipelineColorBlendStateCreateInfo no_color_blend_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_CLEAR,
.attachmentCount = 0,
.pAttachments = nullptr,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
? *convert_msaa_to_non_msaa_frag
: *convert_non_msaa_to_msaa_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -1587,42 +1450,37 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &multisample_ci,
.pDepthStencilState = key.is_depth ? &depth_stencil_ci : nullptr,
.pColorBlendState = key.is_depth ? &no_color_blend_ci
: &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDepthStencilState = nullptr,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *msaa_copy_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
}));
return *msaa_copy_pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline,
const Framebuffer* framebuffer) {
ConvertPipeline(pipeline, framebuffer, false);
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
}
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline,
const Framebuffer* framebuffer) {
ConvertPipeline(pipeline, framebuffer, true);
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, true);
}
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture,
bool is_target_depth) {
if (pipeline) {
return;
}
const VkRenderPass renderpass = framebuffer->RenderPass();
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *module);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1641,35 +1499,31 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffe
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache());
});
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline,
const Framebuffer* framebuffer,
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, framebuffer, module, false, false);
ConvertPipelineEx(pipeline, renderpass, module, false, false);
}
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline,
const Framebuffer* framebuffer,
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, framebuffer, module, true, true);
ConvertPipelineEx(pipeline, renderpass, module, true, true);
}
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
bool is_target_depth) {
if (pipeline) {
return;
}
const VkRenderPass renderpass = framebuffer->RenderPass();
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
VkShaderModule frag_shader =
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = renderpass ? nullptr : &rendering_ci,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1688,7 +1542,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer*
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache());
});
}
} // namespace Vulkan
+14 -34
View File
@@ -33,8 +33,6 @@ struct BlitImagePipelineKey {
VkRenderPass renderpass;
Tegra::Engines::Fermi2D::Operation operation;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct BlitDepthStencilPipelineKey {
@@ -45,8 +43,6 @@ struct BlitDepthStencilPipelineKey {
u8 stencil_mask;
u32 stencil_compare_mask;
u32 stencil_ref;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct MSAACopyPipelineKey {
@@ -55,7 +51,6 @@ struct MSAACopyPipelineKey {
VkRenderPass renderpass;
VkSampleCountFlagBits samples;
bool msaa_to_non_msaa;
bool is_depth;
};
struct BlitMSAAPipelineKey {
@@ -63,15 +58,6 @@ struct BlitMSAAPipelineKey {
VkRenderPass renderpass;
VkSampleCountFlagBits samples;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct ResolveDepthStencilPipelineKey {
constexpr auto operator<=>(const ResolveDepthStencilPipelineKey&) const noexcept = default;
VkRenderPass renderpass;
VkFormat depth_format;
};
class BlitImageHelper {
@@ -137,36 +123,31 @@ private:
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer);
const BlitDepthStencilPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(const Framebuffer* framebuffer,
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil);
void ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
bool is_target_depth);
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture, bool is_target_depth);
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module);
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module);
const Device& device;
@@ -199,7 +180,6 @@ private:
vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::ShaderModule convert_msaa_to_non_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -215,9 +195,9 @@ private:
std::vector<vk::Pipeline> msaa_copy_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
std::vector<ResolveDepthStencilPipelineKey> resolve_depth_keys;
std::vector<VkRenderPass> resolve_depth_keys;
std::vector<vk::Pipeline> resolve_depth_pipelines;
std::vector<ResolveDepthStencilPipelineKey> resolve_depth_stencil_keys;
std::vector<VkRenderPass> resolve_depth_stencil_keys;
std::vector<vk::Pipeline> resolve_depth_stencil_pipelines;
struct MSAACopyResources {
u64 tick;
@@ -164,9 +164,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
}
provoking_vertex_last.Assign(use_last_provoking_vertex ? 1 : 0);
if (!features.has_dynamic_state3_conservative_raster_mode) {
conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
}
conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0);
smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0);
alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0);
alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0);
@@ -362,35 +360,18 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
}
bool IsDepthClipEnabled(const Maxwell& regs) {
const auto clip = regs.viewport_clip_control.geometry_clip.Value();
return clip == Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
clip == Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
clip == Maxwell::ViewportClipControl::GeometryClip::FrustumZ;
}
bool IsDepthClampEnabled(const Maxwell& regs, bool has_depth_clip_enable) {
if (!IsDepthClipEnabled(regs)) {
return true;
}
if (!has_depth_clip_enable) {
return false;
}
return regs.viewport_clip_control.pixel_min_z.Value() != 0 ||
regs.viewport_clip_control.pixel_max_z.Value() != 0;
}
void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs,
const DynamicFeatures& features) {
if (!features.has_dynamic_state3_logic_op_enable) {
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
}
if (features.has_depth_clip_enable) {
depth_clip_disabled.Assign(IsDepthClipEnabled(regs) ? 0 : 1);
}
if (!features.has_dynamic_state3_depth_clamp_enable) {
depth_clamp_disabled.Assign(
IsDepthClampEnabled(regs, features.has_depth_clip_enable) ? 0 : 1);
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
}
if (!features.has_dynamic_state3_line_stipple_enable) {
line_stipple_enable.Assign(regs.line_stipple_enable);
@@ -30,8 +30,6 @@ struct DynamicFeatures {
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_state3_depth_clamp_enable;
bool has_dynamic_state3_conservative_raster_mode;
bool has_depth_clip_enable;
bool has_dynamic_state3_logic_op_enable;
bool has_dynamic_state3_line_stipple_enable;
bool has_dynamic_vertex_input;
@@ -167,7 +165,6 @@ struct FixedPipelineState {
BitField<10, 1, u32> logic_op_enable;
BitField<11, 1, u32> depth_clamp_disabled;
BitField<12, 1, u32> line_stipple_enable;
BitField<13, 1, u32> depth_clip_disabled;
};
union {
u32 raw2;
@@ -301,9 +298,6 @@ static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
static_assert(std::is_trivially_constructible_v<FixedPipelineState>);
bool IsDepthClipEnabled(const Maxwell& regs);
bool IsDepthClampEnabled(const Maxwell& regs, bool has_depth_clip_enable);
} // namespace Vulkan
namespace std {
@@ -47,93 +47,6 @@ using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
return std::nullopt;
}
[[nodiscard]] inline VkDeviceSize DescriptorSizeForType(const Device& device,
VkDescriptorType type) {
const auto& props = device.DescriptorBufferProperties();
const bool robust = device.IsRobustBufferAccessEnabled();
switch (type) {
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
return robust ? props.robustUniformBufferDescriptorSize : props.uniformBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
return robust ? props.robustStorageBufferDescriptorSize : props.storageBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
return robust ? props.robustUniformTexelBufferDescriptorSize
: props.uniformTexelBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
return robust ? props.robustStorageTexelBufferDescriptorSize
: props.storageTexelBufferDescriptorSize;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
return props.combinedImageSamplerDescriptorSize;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
return props.storageImageDescriptorSize;
default:
return 0;
}
}
struct DescriptorBufferBinding {
VkDescriptorType type;
u32 count;
VkDeviceSize offset;
VkDeviceSize stride;
};
struct DescriptorBufferLayout {
VkDeviceSize size{};
boost::container::small_vector<DescriptorBufferBinding, 32> bindings;
[[nodiscard]] bool Empty() const noexcept {
return bindings.empty();
}
};
inline void WriteDescriptorBuffer(const Device& device, const DescriptorBufferLayout& layout,
const DescriptorUpdateEntry* payload, u8* host) {
const vk::Device& dev = device.GetLogical();
for (const DescriptorBufferBinding& binding : layout.bindings) {
for (u32 index = 0; index < binding.count; ++index) {
const DescriptorUpdateEntry& entry = *(payload++);
const VkDescriptorAddressInfoEXT address_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT,
.pNext = nullptr,
.address = entry.address.address,
.range = entry.address.range,
.format = entry.address.format,
};
VkDescriptorGetInfoEXT get_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
.pNext = nullptr,
.type = binding.type,
.data{},
};
switch (binding.type) {
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
get_info.data.pUniformBuffer = &address_info;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
get_info.data.pStorageBuffer = &address_info;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
get_info.data.pUniformTexelBuffer = &address_info;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
get_info.data.pStorageTexelBuffer = &address_info;
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
get_info.data.pCombinedImageSampler = &entry.image;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
get_info.data.pStorageImage = &entry.image;
break;
default:
continue;
}
dev.GetDescriptorEXT(get_info, binding.stride,
host + binding.offset + index * binding.stride);
}
}
}
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
Shader::NumDescriptors(info.storage_buffers_descriptors) +
@@ -152,59 +65,16 @@ public:
num_descriptors <= device->MaxPushDescriptors();
}
bool CanUseDescriptorBuffer() const noexcept {
return device->IsExtDescriptorBufferSupported() && !bindings.empty() &&
!CanUsePushDescriptor() &&
device->DescriptorBufferProperties().combinedImageSamplerDescriptorSingleArray;
}
DescriptorBufferLayout MakeDescriptorBufferLayout(VkDescriptorSetLayout layout) const {
DescriptorBufferLayout result;
if (!layout) {
return result;
}
const vk::Device& dev = device->GetLogical();
result.size = dev.GetDescriptorSetLayoutSizeEXT(layout);
result.bindings.reserve(bindings.size());
for (const VkDescriptorSetLayoutBinding& binding : bindings) {
result.bindings.push_back(DescriptorBufferBinding{
.type = binding.descriptorType,
.count = binding.descriptorCount,
.offset = dev.GetDescriptorSetLayoutBindingOffsetEXT(layout, binding.binding),
.stride = DescriptorSizeForType(*device, binding.descriptorType),
});
}
return result;
}
vk::DescriptorSetLayout CreateDescriptorSetLayout(bool use_push_descriptor,
bool use_descriptor_buffer = false) const {
// TODO(crueter): utilize layout binding flags
vk::DescriptorSetLayout CreateDescriptorSetLayout(bool use_push_descriptor) const {
if (bindings.empty()) {
return nullptr;
}
VkDescriptorSetLayoutCreateFlags flags = 0;
if (use_push_descriptor) {
flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
}
if (use_descriptor_buffer) {
flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
}
boost::container::small_vector<VkDescriptorBindingFlags, 32> binding_flags;
VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{};
const void* pnext = nullptr;
if (!use_push_descriptor && device->IsDescriptorBindingPartiallyBoundSupported()) {
binding_flags.assign(bindings.size(), VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT);
binding_flags_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.pNext = nullptr,
.bindingCount = static_cast<u32>(binding_flags.size()),
.pBindingFlags = binding_flags.data(),
};
pnext = &binding_flags_ci;
}
const VkDescriptorSetLayoutCreateFlags flags =
use_push_descriptor ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR : 0;
return device->GetLogical().CreateDescriptorSetLayout({
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = pnext,
.pNext = nullptr,
.flags = flags,
.bindingCount = static_cast<u32>(bindings.size()),
.pBindings = bindings.data(),
@@ -491,7 +491,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.subpass = 0,
.basePipelineHandle = 0,
.basePipelineIndex = 0,
}, device.StaticPipelineCache());
});
}
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
@@ -69,9 +69,6 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
if (device.IsExtConditionalRendering()) {
flags |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
}
if (device.IsBufferDeviceAddressSupported()) {
flags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -94,9 +91,6 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
device = &runtime.device;
buffer = runtime.CreateNullBuffer();
is_null = true;
if (device->IsBufferDeviceAddressSupported()) {
device_address = device->GetLogical().GetBufferDeviceAddress(*buffer);
}
}
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
@@ -106,9 +100,6 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
if (runtime.device.HasDebuggingToolAttached()) {
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
}
if (device->IsBufferDeviceAddressSupported()) {
device_address = device->GetLogical().GetBufferDeviceAddress(*buffer);
}
}
void Buffer::MarkUsage(u64 offset, u64 size) noexcept {
@@ -255,6 +246,7 @@ protected:
StagingBufferPool& staging_pool;
vk::Buffer buffer{};
MemoryCommit memory_commit{};
VkIndexType index_type{};
u32 num_indices = 0;
};
@@ -364,16 +356,6 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
scheduler_, staging_pool_);
}
void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size,
std::span<AHardwareBuffer* const> hardware_buffers,
size_t hardware_buffer_window) {
unified_memory = std::make_unique<HostMemoryImport>(device, base, size, hardware_buffers,
hardware_buffer_window);
if (!unified_memory->IsValid()) {
unified_memory.reset();
}
}
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
return staging_pool.Request(size, MemoryUsage::Upload);
}
@@ -382,10 +364,6 @@ StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool def
return staging_pool.Request(size, MemoryUsage::Download, deferred);
}
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
return MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, false, format).format;
}
void BufferCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
staging_pool.FreeDeferred(ref);
}
@@ -398,10 +376,6 @@ u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
return device.GetDeviceMemoryUsage();
}
u64 BufferCacheRuntime::GetDeviceAllocationUsage() const {
return device.GetMemoryBudgetInfo().allocation_bytes;
}
bool BufferCacheRuntime::CanReportMemoryUsage() const {
return device.CanReportMemoryUsage();
}
@@ -430,16 +404,6 @@ u64 BufferCacheRuntime::KnownGpuTick() {
return scheduler.GetMasterSemaphore().KnownGpuTick();
}
u64 BufferCacheRuntime::CurrentSyncPoint() const noexcept {
return scheduler.GetMasterSemaphore().CurrentTick();
}
u64 BufferCacheRuntime::CompletedSyncPoint() const {
auto& master_semaphore = scheduler.GetMasterSemaphore();
master_semaphore.Refresh();
return master_semaphore.KnownGpuTick();
}
void BufferCacheRuntime::Wait(u64 buffer_tick) {
scheduler.Wait(buffer_tick);
}
@@ -677,7 +641,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
offset = 0;
size = 0;
}
scheduler.MarkTransformFeedbackUsed();
scheduler.Record([index, buffer, offset, size](vk::CommandBuffer cmdbuf) {
const VkDeviceSize vk_offset = offset;
const VkDeviceSize vk_size = size;
@@ -690,26 +653,19 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
// Already logged in the rasterizer
return;
}
const u32 count = std::min<u32>(static_cast<u32>(bindings.buffers.size()),
VideoCommon::NUM_TRANSFORM_FEEDBACK_BUFFERS);
std::array<VkBuffer, VideoCommon::NUM_TRANSFORM_FEEDBACK_BUFFERS> handles{};
std::array<VkDeviceSize, VideoCommon::NUM_TRANSFORM_FEEDBACK_BUFFERS> offsets{};
std::array<VkDeviceSize, VideoCommon::NUM_TRANSFORM_FEEDBACK_BUFFERS> sizes{};
for (u32 i = 0; i < count; ++i) {
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
auto handle = bindings.buffers[i]->Handle();
if (handle == VK_NULL_HANDLE) {
ReserveNullBuffer();
handle = *null_buffer;
} else {
offsets[i] = bindings.offsets[i];
sizes[i] = bindings.sizes[i];
bindings.offsets[i] = 0;
bindings.sizes[i] = 0;
}
handles[i] = handle;
buffer_handles[i] = handle;
}
scheduler.MarkTransformFeedbackUsed();
scheduler.Record([count, handles, offsets, sizes](vk::CommandBuffer cmdbuf) {
cmdbuf.BindTransformFeedbackBuffersEXT(0, count, handles.data(), offsets.data(),
sizes.data());
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
cmdbuf.BindTransformFeedbackBuffersEXT(0, u32(buffer_handles_.size()), buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data());
});
}
@@ -734,9 +690,6 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
if (device.IsExtTransformFeedbackSupported()) {
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
if (device.IsBufferDeviceAddressSupported()) {
create_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
if (device.HasDebuggingToolAttached()) {
ret.SetObjectNameEXT("Null buffer");
@@ -7,8 +7,6 @@
#pragma once
#include <limits>
#include <memory>
#include <span>
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h"
@@ -41,10 +39,6 @@ public:
return *buffer;
}
[[nodiscard]] VkDeviceAddress DeviceAddress() const noexcept {
return device_address;
}
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
return tracker.IsUsed(offset, size);
}
@@ -76,7 +70,6 @@ private:
vk::Buffer buffer;
std::vector<BufferView> views;
VideoCommon::UsageTracker tracker;
VkDeviceAddress device_address{};
u64 last_usage_tick{};
bool is_null{};
};
@@ -99,26 +92,6 @@ public:
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
void TryEnableUnifiedMemory(void* base, size_t size,
std::span<AHardwareBuffer* const> hardware_buffers,
size_t hardware_buffer_window);
[[nodiscard]] bool HasUnifiedMemory() const noexcept {
return unified_memory != nullptr && unified_memory->IsValid();
}
[[nodiscard]] u64 UnifiedMemorySize() const noexcept {
return unified_memory ? unified_memory->GetSize() : 0;
}
[[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept {
return unified_memory ? unified_memory->GetWindowSize() : 0;
}
[[nodiscard]] VkBuffer UnifiedMemoryWindowBuffer(size_t index) const noexcept {
return unified_memory ? unified_memory->GetWindowBuffer(index) : VK_NULL_HANDLE;
}
u64 CurrentTick();
u64 KnownGpuTick();
@@ -127,16 +100,10 @@ public:
void Finish();
u64 CurrentSyncPoint() const noexcept;
u64 CompletedSyncPoint() const;
u64 GetDeviceLocalMemory() const;
u64 GetDeviceMemoryUsage() const;
u64 GetDeviceAllocationUsage() const;
bool CanReportMemoryUsage() const;
u32 GetUniformBufferAlignment() const;
@@ -178,25 +145,22 @@ public:
[[maybe_unused]] u32 binding_index,
u32 size) {
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
static_cast<u32>(ref.offset), size);
BindBuffer(ref.buffer, static_cast<u32>(ref.offset), size);
return ref.mapped_span;
}
void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) {
void BindUniformBuffer(VkBuffer buffer, u32 offset, u32 size) {
BindBuffer(buffer, offset, size);
}
void BindStorageBuffer(const Buffer& buffer, u32 offset, u32 size,
void BindStorageBuffer(VkBuffer buffer, u32 offset, u32 size,
[[maybe_unused]] bool is_written) {
BindBuffer(buffer, offset, size);
}
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
VideoCore::Surface::PixelFormat format) {
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format),
buffer.DeviceAddress(), offset, size,
TexelBufferFormat(format));
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format));
}
bool ShouldLimitDynamicStorageBuffers() const {
@@ -208,17 +172,14 @@ public:
}
private:
void BindBuffer(const Buffer& buffer, u32 offset, u32 size) {
const VkBuffer handle = buffer.Handle();
if (handle == VK_NULL_HANDLE) {
guest_descriptor_queue.AddBuffer(handle, 0, 0, VK_WHOLE_SIZE);
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
if (buffer == VK_NULL_HANDLE) {
guest_descriptor_queue.AddBuffer(buffer, 0, VK_WHOLE_SIZE);
} else {
guest_descriptor_queue.AddBuffer(handle, buffer.DeviceAddress(), offset, size);
guest_descriptor_queue.AddBuffer(buffer, offset, size);
}
}
VkFormat TexelBufferFormat(VideoCore::Surface::PixelFormat format) const;
void ReserveNullBuffer();
vk::Buffer CreateNullBuffer();
@@ -232,7 +193,6 @@ private:
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
vk::Buffer null_buffer;
std::unique_ptr<HostMemoryImport> unified_memory;
std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass;
@@ -255,7 +215,6 @@ struct BufferCacheParams {
static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
static constexpr bool USE_UNIFIED_MEMORY = true;
};
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
@@ -1,13 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstddef>
#include "video_core/renderer_vulkan/vk_command_pool.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -18,52 +14,32 @@ constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
struct CommandPool::Pool {
vk::CommandPool handle;
vk::CommandBuffers cmdbufs;
u64 tick;
};
CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_)
: master_semaphore{master_semaphore_}, device{device_} {}
: ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {}
CommandPool::~CommandPool() = default;
void CommandPool::AllocatePool() {
void CommandPool::Allocate(size_t begin, size_t end) {
// Command buffers are going to be committed, recorded, executed every single usage cycle.
// They are also going to be reset when committed.
Pool& pool = pools.emplace_back();
pool.handle = device.GetLogical().CreateCommandPool({
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.flags =
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = device.GetGraphicsFamily(),
});
pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
pool.tick = 0;
}
void CommandPool::AcquirePool() {
if (!pools.empty()) {
master_semaphore.Refresh();
const u64 gpu_tick = master_semaphore.KnownGpuTick();
for (size_t i = 0; i < pools.size(); ++i) {
const size_t candidate = (current_pool + 1 + i) % pools.size();
if (gpu_tick >= pools[candidate].tick) {
current_pool = candidate;
current_index = 0;
pools[current_pool].handle.Reset();
return;
}
}
}
AllocatePool();
current_pool = pools.size() - 1;
current_index = 0;
}
VkCommandBuffer CommandPool::Commit() {
if (pools.empty() || current_index >= COMMAND_BUFFER_POOL_SIZE) {
AcquirePool();
}
Pool& pool = pools[current_pool];
pool.tick = master_semaphore.CurrentTick();
return pool.cmdbufs[current_index++];
const size_t index = CommitResource();
const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
return pools[pool_index].cmdbufs[sub_index];
}
} // namespace Vulkan
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,7 +6,7 @@
#include <cstddef>
#include <vector>
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@@ -17,24 +14,20 @@ namespace Vulkan {
class Device;
class MasterSemaphore;
class CommandPool final {
class CommandPool final : public ResourcePool {
public:
explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_);
~CommandPool();
~CommandPool() override;
void Allocate(size_t begin, size_t end) override;
VkCommandBuffer Commit();
private:
struct Pool;
void AllocatePool();
void AcquirePool();
MasterSemaphore& master_semaphore;
const Device& device;
std::vector<Pool> pools;
size_t current_pool = 0;
size_t current_index = 0;
};
} // namespace Vulkan

Some files were not shown because too many files have changed in this diff Show More