Compare commits

...

3 Commits

Author SHA1 Message Date
wildcard fbf96ad587 [vulkan] skip staging buffer for uploads for UMA
initial implementation to skip staging buffer for uploads for unified memory access devices like android and igpus.
2026-02-09 11:02:15 +01:00
MrPurple666 866881d0e3 [android] add FD_DEV_FEATURES in env loader (#3493)
This environment variable fixes some glitches in OneUI 7 and HyperOS 3.

Thanks StevenMX for letting me know.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3493
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: MrPurple666 <antoniosacramento666usa@gmail.com>
Co-committed-by: MrPurple666 <antoniosacramento666usa@gmail.com>
2026-02-09 04:26:43 +01:00
lizzie a56b8d3de8 [core, windows] remove microSleep() and simply wait on events like on linux (#3498)
we shall see if the original code was put there for a reason

or if the microsleeps actually are horrid

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3498
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 01:14:32 +01:00
12 changed files with 85 additions and 139 deletions
@@ -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
package org.yuzu.yuzu_emu.fragments
@@ -110,7 +110,7 @@ class FreedrenoSettingsFragment : Fragment() {
val commonVars = listOf(
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
"TU_BREADCRUMBS"
"TU_BREADCRUMBS", "FD_DEV_FEATURES"
)
for (varName in commonVars) {
@@ -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
package org.yuzu.yuzu_emu.utils
@@ -144,6 +144,15 @@ object FreedrenoPresets {
)
)
val DEV_FEATURES_UBWC_HINT = FreedrenoPreset(
name = "Dev - UBWC Flag Hint",
description = "Enable TP UBWC flag hint for development",
icon = "ic_dev_features",
variables = mapOf(
"FD_DEV_FEATURES" to "enable_tp_ubwc_flag_hint=1"
)
)
val PERFORMANCE_DEFAULT = FreedrenoPreset(
name = "Performance - Default",
description = "Clear all debug options for performance",
@@ -159,6 +168,7 @@ object FreedrenoPresets {
CAPTURE_FRAMES,
SHADER_DEBUG,
GPU_HANG_TRACE,
DEV_FEATURES_UBWC_HINT,
PERFORMANCE_DEFAULT
)
}
@@ -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
/**
@@ -258,6 +258,11 @@ Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoEnv(
}
LOG_INFO(Frontend, "[Freedreno] Set {}={}", var_name, value);
if (var_name == "FD_DEV_FEATURES") {
LOG_INFO(Frontend, "[Freedreno] FD_DEV_FEATURES enabled: {}", value);
}
return JNI_TRUE;
}
+1 -3
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: 2018 yuzu Emulator Project SPDX-License-Identifier:
@@ -186,8 +186,6 @@ if(ARCHITECTURE_x86_64)
common
PRIVATE x64/cpu_detect.cpp
x64/cpu_detect.h
x64/cpu_wait.cpp
x64/cpu_wait.h
x64/native_clock.cpp
x64/native_clock.h
x64/rdtsc.cpp
-78
View File
@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/x64/cpu_detect.h"
#include "common/x64/cpu_wait.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
namespace {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
constexpr auto PauseCycles = 100'000U;
} // Anonymous namespace
#if defined(_MSC_VER) && !defined(__clang__)
__forceinline static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
}
__forceinline static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
_mm_monitorx(&monitor_var, 0, 0);
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
}
#else
static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
const auto tsc = FencedRDTSC() + PauseCycles;
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
const auto edx = static_cast<u32>(tsc >> 32);
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
}
static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
}
#endif
void MicroSleep() {
static const bool has_waitpkg = GetCPUCaps().waitpkg;
static const bool has_monitorx = GetCPUCaps().monitorx;
if (has_waitpkg) {
TPAUSE();
} else if (has_monitorx) {
MWAITX();
} else {
std::this_thread::yield();
}
}
} // namespace Common::X64
-10
View File
@@ -1,10 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Common::X64 {
void MicroSleep();
} // namespace Common::X64
+2 -27
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 2020 yuzu Emulator Project
@@ -13,10 +13,6 @@
#include "common/windows/timer_resolution.h"
#endif
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_wait.h"
#endif
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hardware_properties.h"
@@ -287,28 +283,7 @@ void CoreTiming::ThreadLoop() {
if (next_time) {
// There are more events left in the queue, wait until the next event.
auto wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time > 0) {
#ifdef _WIN32
while (!paused && !event.IsSet() && wait_time > 0) {
wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time >= timer_resolution_ns) {
Common::Windows::SleepForOneTick();
} else {
#ifdef ARCHITECTURE_x86_64
Common::X64::MicroSleep();
#else
std::this_thread::yield();
#endif
}
}
if (event.IsSet()) {
event.Reset();
}
#else
event.WaitFor(std::chrono::nanoseconds(wait_time));
#endif
}
event.WaitFor(std::chrono::nanoseconds(wait_time));
} else {
// Queue is empty, wait until another event is scheduled and signals us to
// continue.
+37 -16
View File
@@ -739,12 +739,18 @@ void BufferCache<P>::BindHostIndexBuffer() {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
auto upload_staging = runtime.UploadStagingBuffer(size);
std::array<BufferCopy, 1> copies{
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(),
draw_state.inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
if (buffer.IsHostVisible()) {
// write directly to mapped buffer
std::memcpy(buffer.Mapped().data(),
draw_state.inline_index_draw_indexes.data(), size);
} else {
auto upload_staging = runtime.UploadStagingBuffer(size);
std::array<BufferCopy, 1> copies{
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(),
draw_state.inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
}
} else {
buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
}
@@ -1590,6 +1596,15 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] u64 total_size_bytes,
[[maybe_unused]] std::span<BufferCopy> copies) {
if constexpr (USE_MEMORY_MAPS) {
if (buffer.IsHostVisible() && runtime.CanReorderUpload(buffer, copies)) {
const std::span<u8> mapped_span = buffer.Mapped();
for (const BufferCopy& copy : copies) {
u8* const dst_pointer = mapped_span.data() + copy.dst_offset;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
device_memory.ReadBlockUnsafe(device_addr, dst_pointer, copy.size);
}
return;
}
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
const std::span<u8> staging_pointer = upload_staging.mapped_span;
for (BufferCopy& copy : copies) {
@@ -1634,16 +1649,22 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
std::array copies{BufferCopy{
.src_offset = upload_staging.offset,
.dst_offset = buffer.Offset(dest_address),
.size = copy_size,
}};
u8* const src_pointer = upload_staging.mapped_span.data();
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
const u32 buffer_offset = buffer.Offset(dest_address);
if (buffer.IsHostVisible()) {
// write directly to mapped buffer
std::memcpy(buffer.Mapped().data() + buffer_offset, inlined_buffer.data(), copy_size);
} else {
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
std::array copies{BufferCopy{
.src_offset = upload_staging.offset,
.dst_offset = buffer_offset,
.size = copy_size,
}};
u8* const src_pointer = upload_staging.mapped_span.data();
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
}
} else {
buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
}
@@ -34,6 +34,14 @@ public:
void MarkUsage(u64 offset, u64 size) {}
[[nodiscard]] bool IsHostVisible() const noexcept {
return false;
}
[[nodiscard]] std::span<u8> Mapped() noexcept {
return {};
}
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
[[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
@@ -43,6 +43,14 @@ public:
return tracker.IsUsed(offset, size);
}
[[nodiscard]] bool IsHostVisible() const noexcept {
return buffer.IsHostVisible();
}
[[nodiscard]] std::span<u8> Mapped() noexcept {
return buffer.Mapped();
}
void MarkUsage(u64 offset, u64 size) noexcept {
tracker.Track(offset, size);
}
@@ -798,6 +798,10 @@ public:
return must_emulate_scaled_formats;
}
bool HasUnifiedMemory() const {
return is_integrated;
}
bool HasNullDescriptor() const {
return features.robustness2.nullDescriptor;
}
@@ -259,7 +259,7 @@ namespace Vulkan {
vk::Buffer
MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const
{
const VmaAllocationCreateInfo alloc_ci = {
VmaAllocationCreateInfo alloc_ci = {
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
.usage = MemoryUsageVma(usage),
.requiredFlags = 0,
@@ -270,6 +270,11 @@ namespace Vulkan {
.priority = 0.f,
};
if (device.HasUnifiedMemory() && usage == MemoryUsage::DeviceLocal) {
alloc_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
alloc_ci.preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
VkBuffer handle{};
VmaAllocationInfo alloc_info{};
VmaAllocation allocation{};