Compare commits

...

9 commits

Author SHA1 Message Date
lizzie
3eb537ed82 [video_core/engines/maxwell3d] memory inline DrawState to reduce indirection on hot paths
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-03-26 04:35:38 +01:00
xbzk
f0d77e86e3
[android,ui] driver management: fixed driver add/removal unsync (#3757)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Complementary for 3750.
User found a way to get same driver doubled and deleting one would lead to a crash.
Reason: manual driver install was still adding drivers directly to adapter, instead of thru drivermodel. fixed.
Also added guards against crash upon driver removal.
Thoroughly tested.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3757
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-25 12:13:04 +01:00
lizzie
24fe223692
[dynarmic] Remove last FPT LUT table, removing around 30kb worth of unused functions (#3718)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Lets do the quick math
There was 1 LUT for every fsize() instancing

Now... the number of functions on each lut was (fsize + 1), multiplied by 5 (number of rounding modes)

8 = 9 * 5 = 45
16 = 17 * 5 = 85
32 = 33 * 5 = 165
64 = 65 * 5 = 325

this is just pure insanity - look at what fucking nm reported:

```
0000000003dc39b8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<16ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a18 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<16ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc39d8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<32ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a38 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<32ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc39f8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<64ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a58 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<64ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
```

"ah its not bad" - OH MATE ITS JUST THE GUARD VARIABLES - i attached a file with just the functions generated for each case...

now with this PR only 6 * 6 functions are made (still not ideal, but way better), 36 is way better than 1156 FUCKING FUNCTIONS

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3718
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-25 10:48:53 +01:00
PavelBARABANOV
8f770618d2
[android] Rework of frame pacing mode + Surface mode detection per API level. (#3735)
Some checks failed
tx-src / sources (push) Has been cancelled
Check Strings / check-strings (push) Has been cancelled
This Pr is a reply to certain issues found on Android due to the new artificial waits inside Vulkan (Frame Pacing Mode); which caused GPU/CPU desync's even if TimelineSemaphore (Adreno's drivers) does a constant check to retain synchronization with each frame-data, removes the yield() for all platforms (remains the same on PC) and aligns a new way to handle the output of video by using native Android tools, such as AGP, which makes a bridge inside Vulkan to Android's Surface (screen) and reduces not only the latency, but also improves the smoothness of each frame processed; currently we quantize the amount of frame processed by hinting the surface on Android space and adjust the heuristics of the old handling (yuzu) and we link it to screen refresh rate; this way we ensure that even if the game moves below the screen's HZ, we can always pick up the cadence by clamping the duration of each frame and using a chrono function to work as internal fernce if performance goes below the game speed requirment or game's frame rate requirements.

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3735
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-03-24 01:25:44 +01:00
lizzie
b673dad40d
[hle/service/nifm] fix pack(pop) warning on clang (#3764)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
fucks up unity builds, also it's an innocuous trivial change for a warning that should've been fixed a while ago

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3764
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-23 18:03:54 +01:00
lizzie
56d3f0e353
[dynarmic] fix dynarmic_tests build issues on xcode due to using relative paths (#3765)
Thanks to @chrelliott978 for the initial impl

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3765
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-23 18:03:48 +01:00
crueter
ad58ab8976
[externals] Use Eden mirrors for llvm-mingw and tzdb (#3766)
My server is getting hammered, let's just move them here.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3766
2026-03-23 17:05:40 +01:00
lizzie
772e38cb8d
[hle/service/sockets] fix hogwarts legacy crash due to non-blacklisted domain (#3762)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3762
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-22 20:17:44 +01:00
lizzie
811cc18d74
[hle/acc] fix (false) return where it's just 2 (#3763)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3763
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-22 17:37:21 +01:00
56 changed files with 648 additions and 586 deletions

View file

@ -87,8 +87,8 @@
"bundled": true
},
"llvm-mingw": {
"repo": "misc/llvm-mingw",
"git_host": "git.crueter.xyz",
"repo": "eden-emu/llvm-mingw",
"git_host": "git.eden-emu.dev",
"tag": "%VERSION%",
"version": "20250828",
"artifact": "clang-rt-builtins.tar.zst",

View file

@ -246,12 +246,13 @@
},
"tzdb": {
"package": "nx_tzdb",
"repo": "misc/tzdb_to_nx",
"git_host": "git.crueter.xyz",
"repo": "eden-emu/tzdb_to_nx",
"git_host": "git.eden-emu.dev",
"artifact": "%VERSION%.tar.gz",
"tag": "%VERSION%",
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125"
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
"version": "121125",
"git_version": "230326"
},
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",

View file

@ -670,15 +670,6 @@ abstract class SettingsItem(
valuesId = R.array.dmaAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.FRAME_PACING_MODE,
titleId = R.string.frame_pacing_mode,
descriptionId = R.string.frame_pacing_mode_description,
choicesId = R.array.framePacingModeNames,
valuesId = R.array.framePacingModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,

View file

@ -256,7 +256,6 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.DMA_ACCURACY.key)
add(IntSetting.FRAME_PACING_MODE.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)

View file

@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
@ -216,19 +215,23 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
driverViewModel.driverData.firstOrNull { it.second == driverData }
driverViewModel.driverData.firstOrNull {
it.first == driverPath || it.second == driverData
}
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
withContext(Dispatchers.Main) {
if (_binding != null) {
refreshDriverList()
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
val selectedPosition = adapter.currentList
.indexOfFirst { it.selected }
.let { if (it == -1) 0 else it }
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
.smoothScrollToPosition(selectedPosition)
}
}
}

View file

@ -154,15 +154,30 @@ class DriverViewModel : ViewModel() {
}
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
driversToDelete.add(driverData[removedPosition - 1].first)
driverData.removeAt(removedPosition - 1)
onDriverSelected(selectedPosition)
val driverIndex = removedPosition - 1
if (driverIndex !in driverData.indices) {
updateDriverList()
return
}
driversToDelete.add(driverData[driverIndex].first)
driverData.removeAt(driverIndex)
val safeSelectedPosition = selectedPosition.coerceIn(0, driverData.size)
onDriverSelected(safeSelectedPosition)
}
fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
if (driversToDelete.contains(driver.first)) {
driversToDelete.remove(driver.first)
}
val existingDriverIndex = driverData.indexOfFirst {
it.first == driver.first || it.second == driver.second
}
if (existingDriverIndex != -1) {
onDriverSelected(existingDriverIndex + 1)
return
}
driverData.add(driver)
onDriverSelected(driverData.size)
}

View file

@ -86,8 +86,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
// Since Android 15, google automatically forces "games" to be 60 hrz
// This ensures the display's max refresh rate is actually used
display?.let {
val supportedModes = it.supportedModes
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }

View file

@ -6,8 +6,15 @@
#include <android/native_window_jni.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <dlfcn.h>
#include "common/android/id_cache.h"
#include "common/logging.h"
#include "common/settings.h"
#include "input_common/drivers/android.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
@ -22,6 +29,12 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = 0;
m_window_height = 0;
window_info.render_surface = nullptr;
m_last_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint_votes = 0;
m_smoothed_present_rate = 0.0f;
m_last_frame_display_time = {};
m_pending_frame_rate_since = {};
return;
}
@ -32,6 +45,7 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
window_info.render_surface = reinterpret_cast<void*>(surface);
UpdateFrameRateHint();
}
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
@ -51,6 +65,9 @@ void EmuWindow_Android::OnTouchReleased(int id) {
}
void EmuWindow_Android::OnFrameDisplayed() {
UpdateObservedFrameRate();
UpdateFrameRateHint();
if (!m_first_frame) {
Common::Android::RunJNIOnFiber<void>(
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
@ -58,6 +75,175 @@ void EmuWindow_Android::OnFrameDisplayed() {
}
}
void EmuWindow_Android::UpdateObservedFrameRate() {
const auto now = Clock::now();
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
const float seconds = frame_time.count();
if (seconds > 0.0f) {
const float instantaneous_rate = 1.0f / seconds;
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
instantaneous_rate <= 240.0f) {
constexpr float SmoothingFactor = 0.15f;
if (m_smoothed_present_rate <= 0.0f) {
m_smoothed_present_rate = instantaneous_rate;
} else {
m_smoothed_present_rate +=
(instantaneous_rate - m_smoothed_present_rate) * SmoothingFactor;
}
}
}
}
m_last_frame_display_time = now;
}
float EmuWindow_Android::QuantizeFrameRateHint(float frame_rate) {
if (!std::isfinite(frame_rate) || frame_rate <= 0.0f) {
return 0.0f;
}
frame_rate = std::clamp(frame_rate, 1.0f, 240.0f);
constexpr float Step = 0.5f;
return std::round(frame_rate / Step) * Step;
}
float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
if (!EmulationSession::GetInstance().IsRunning()) {
return 0.0f;
}
const double frame_time_scale =
EmulationSession::GetInstance().System().GetPerfStats().GetLastFrameTimeScale();
if (!std::isfinite(frame_time_scale) || frame_time_scale <= 0.0) {
return 0.0f;
}
const float verified_rate =
std::clamp(60.0f / static_cast<float>(frame_time_scale), 0.0f, 240.0f);
return QuantizeFrameRateHint(verified_rate);
}
float EmuWindow_Android::GetFrameRateHint() const {
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
const float frame_time_verified_hint = GetFrameTimeVerifiedHint();
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
if (std::fabs(observed_rate - m_last_frame_rate_hint) <= tolerance) {
return m_last_frame_rate_hint;
}
}
const float observed_hint = QuantizeFrameRateHint(observed_rate);
if (observed_hint > 0.0f) {
if (frame_time_verified_hint > 0.0f) {
const float tolerance = std::max(observed_hint * 0.20f, 3.0f);
if (std::fabs(observed_hint - frame_time_verified_hint) <= tolerance) {
return QuantizeFrameRateHint((observed_hint + frame_time_verified_hint) * 0.5f);
}
}
return observed_hint;
}
if (frame_time_verified_hint > 0.0f) {
return frame_time_verified_hint;
}
constexpr float NominalFrameRate = 60.0f;
if (!Settings::values.use_speed_limit.GetValue()) {
return NominalFrameRate;
}
const u16 speed_limit = Settings::SpeedLimit();
if (speed_limit == 0) {
return 0.0f;
}
const float speed_limited_rate =
NominalFrameRate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
return QuantizeFrameRateHint(speed_limited_rate);
}
void EmuWindow_Android::UpdateFrameRateHint() {
auto* const surface = reinterpret_cast<ANativeWindow*>(window_info.render_surface);
if (!surface) {
return;
}
const auto now = Clock::now();
const float frame_rate_hint = GetFrameRateHint();
if (std::fabs(frame_rate_hint - m_last_frame_rate_hint) < 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
return;
}
if (frame_rate_hint == 0.0f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = now;
} else if (m_last_frame_rate_hint >= 0.0f) {
if (std::fabs(frame_rate_hint - m_pending_frame_rate_hint) >= 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 1;
m_pending_frame_rate_since = now;
return;
}
++m_pending_frame_rate_hint_votes;
if (m_pending_frame_rate_since.time_since_epoch().count() == 0) {
m_pending_frame_rate_since = now;
}
const auto stable_for = now - m_pending_frame_rate_since;
const float reference_rate = std::max(frame_rate_hint, 1.0f);
const auto stable_duration = std::chrono::duration_cast<Clock::duration>(
std::chrono::duration<float>(std::clamp(3.0f / reference_rate, 0.15f, 0.40f)));
constexpr std::uint32_t MinStableVotes = 3;
if (m_pending_frame_rate_hint_votes < MinStableVotes || stable_for < stable_duration) {
return;
}
} else {
m_pending_frame_rate_since = now;
}
using SetFrameRateWithChangeStrategyFn =
int32_t (*)(ANativeWindow*, float, int8_t, int8_t);
using SetFrameRateFn = int32_t (*)(ANativeWindow*, float, int8_t);
static const auto set_frame_rate_with_change_strategy =
reinterpret_cast<SetFrameRateWithChangeStrategyFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRateWithChangeStrategy"));
static const auto set_frame_rate = reinterpret_cast<SetFrameRateFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRate"));
constexpr int8_t FrameRateCompatibilityDefault = 0;
constexpr int8_t ChangeFrameRateOnlyIfSeamless = 0;
int32_t result = -1;
if (set_frame_rate_with_change_strategy) {
result = set_frame_rate_with_change_strategy(surface, frame_rate_hint,
FrameRateCompatibilityDefault,
ChangeFrameRateOnlyIfSeamless);
} else if (set_frame_rate) {
result = set_frame_rate(surface, frame_rate_hint, FrameRateCompatibilityDefault);
} else {
return;
}
if (result != 0) {
LOG_DEBUG(Frontend, "Failed to update Android surface frame rate hint: {}", result);
return;
}
m_last_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
}
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library)
: m_driver_library{driver_library} {

View file

@ -1,8 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
@ -50,10 +55,24 @@ public:
};
private:
using Clock = std::chrono::steady_clock;
void UpdateFrameRateHint();
void UpdateObservedFrameRate();
[[nodiscard]] float GetFrameRateHint() const;
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
float m_window_width{};
float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
float m_last_frame_rate_hint = -1.0f;
float m_pending_frame_rate_hint = -1.0f;
float m_smoothed_present_rate = 0.0f;
Clock::time_point m_last_frame_display_time{};
Clock::time_point m_pending_frame_rate_since{};
std::uint32_t m_pending_frame_rate_hint_votes = 0;
};

View file

@ -712,9 +712,8 @@ public:
private:
void CheckAvailability(HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(false); // TODO: Check when this is supposed to return true and when not
}
void GetAccountId(HLERequestContext& ctx) {

View file

@ -183,8 +183,8 @@ struct NifmWirelessSettingData {
static_assert(sizeof(NifmWirelessSettingData) == 0x70,
"NifmWirelessSettingData has incorrect size.");
#pragma pack(push, 1)
// This is nn::nifm::detail::sf::NetworkProfileData
#pragma pack(push, 1)
struct SfNetworkProfileData {
IpSettingData ip_setting_data{};
u128 uuid{};
@ -196,9 +196,11 @@ struct SfNetworkProfileData {
SfWirelessSettingData wireless_setting_data{};
INSERT_PADDING_BYTES(1);
};
#pragma pack(pop)
static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
// This is nn::nifm::NetworkProfileData
#pragma pack(push, 1)
struct NifmNetworkProfileData {
u128 uuid{};
std::array<char, 0x40> network_name{};
@ -210,8 +212,8 @@ struct NifmNetworkProfileData {
NifmWirelessSettingData wireless_setting_data{};
IpSettingData ip_setting_data{};
};
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
"NifmNetworkProfileData has incorrect size.");
#pragma pack(pop)
static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size.");
struct PendingProfile {
std::array<char, 0x21> ssid{};

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 2018 yuzu Emulator Project
@ -53,12 +53,15 @@ enum class NetDbError : s32 {
NoData = 4,
};
static const constexpr std::array blockedDomains = {"srv.nintendo.net",
"battle.net",
"microsoft.com",
"mojang.com",
"xboxlive.com",
"minecraftservices.com"};
static const constexpr std::array blockedDomains = {
"srv.nintendo.net", //obvious
"phoenix-api.wbagora.com", //hogwarts legacy
"battle.net",
"microsoft.com", //minecraft dungeons + other games
"mojang.com",
"xboxlive.com",
"minecraftservices.com"
};
static bool IsBlockedHost(const std::string& host) {
return std::any_of(

View file

@ -53,7 +53,6 @@ add_library(dynarmic STATIC
common/fp/util.h
common/llvm_disassemble.cpp
common/llvm_disassemble.h
common/lut_from_list.h
common/math_util.cpp
common/math_util.h
common/safe_ops.h

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
/* This file is part of the dynarmic project.
@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/rounding_mode.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"

View file

@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/rounding_mode.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"

View file

@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/util.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
@ -2127,28 +2126,42 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
}
}
using fbits_list = mp::lift_sequence<std::make_index_sequence<fsize + 1>>;
using rounding_list = mp::list<
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
mp::lift_value<FP::RoundingMode::TowardsZero>,
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
static const auto lut = Common::GenerateLookupTableFromList([]<typename I>(I) {
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
return std::pair{
mp::lower_to_tuple_v<I>,
Common::FptrCast([](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
constexpr size_t fbits = mp::get<0, I>::value;
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
auto const func = [rounding]() -> void(*)(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
switch (rounding) {
case FP::RoundingMode::ToNearest_TieEven:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fbits, unsigned_, fpcr, rounding_mode, fpsr));
})
};
}, mp::cartesian_product<fbits_list, rounding_list>{});
EmitTwoOpFallback<3>(code, ctx, inst, lut.at(std::make_tuple(fbits, rounding)));
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToNearest_TieEven, fpsr));
};
case FP::RoundingMode::TowardsPlusInfinity:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsPlusInfinity, fpsr));
};
case FP::RoundingMode::TowardsMinusInfinity:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsMinusInfinity, fpsr));
};
case FP::RoundingMode::TowardsZero:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsZero, fpsr));
};
case FP::RoundingMode::ToNearest_TieAwayFromZero:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToNearest_TieAwayFromZero, fpsr));
};
case FP::RoundingMode::ToOdd:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToOdd, fpsr));
};
}
}();
EmitTwoOpFallback<3>(code, ctx, inst, func);
}
void EmitX64::EmitFPVectorToSignedFixed16(EmitContext& ctx, IR::Inst* inst) {

View file

@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <initializer_list>
#include <map>
#include <type_traits>
#include <mcl/mp/metafunction/apply.hpp>
#include <mcl/mp/typelist/list.hpp>
#include <mcl/type_traits/is_instance_of_template.hpp>
#ifdef _MSC_VER
# include <mcl/mp/typelist/head.hpp>
#endif
namespace Dynarmic::Common {
// prevents this function from printing 56,000 character warning messages
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-stack-usage"
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wno-stack-usage"
#endif
template<typename Function, typename... Values>
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
#ifdef _MSC_VER
using PairT = std::invoke_result_t<Function, mcl::mp::head<mcl::mp::list<Values...>>>;
#else
using PairT = std::common_type_t<std::invoke_result_t<Function, Values>...>;
#endif
using MapT = mcl::mp::apply<std::map, PairT>;
static_assert(mcl::is_instance_of_template_v<std::pair, PairT>);
const std::initializer_list<PairT> pair_array{f(Values{})...};
return MapT(pair_array.begin(), pair_array.end());
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace Dynarmic::Common

View file

@ -20,15 +20,14 @@
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
#include "../fuzz_util.h"
#include "../rand_int.h"
#include "../unicorn_emu/a32_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"
#include "dynarmic/common/variant_util.h"
#include "dynarmic/frontend/A32/ITState.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/a32_types.h"

View file

@ -19,10 +19,10 @@
#include <mcl/bit/bit_field.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "../unicorn_emu/a32_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/FPSCR.h"
#include "dynarmic/frontend/A32/PSR.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
@ -107,7 +107,7 @@ static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Ji
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) && uni.GetCpsr() == jit.Cpsr() && interp_write_records == jit_write_records;
}
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32::UserConfig const& config, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
uni.ClearPageCache();
jit.ClearCache();
@ -145,9 +145,8 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
printf("Failed at execution number %zu\n", run_number);
printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) {
printf("%04x %s\n", test_env.code_mem[i], A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
}
for (size_t i = 0; i < instruction_count; i++)
printf("%04x\n", test_env.code_mem[i]);
printf("\nInitial Register Listing: \n");
for (size_t i = 0; i < initial_regs.size(); i++) {
@ -175,11 +174,14 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
A32::PSR cpsr;
cpsr.T(true);
IR::Block ir_block{A32::LocationDescriptor{0, {}, {}}};
size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) {
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
Optimization::Optimize(ir_block, &test_env, {});
ir_block.Reset(descriptor);
A32::Translate(ir_block, descriptor, &test_env, {});
Optimization::Optimize(ir_block, config, {});
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
printf("\n\nx86_64:\n");
printf("%s", jit.Disassemble().c_str());
@ -201,8 +203,9 @@ void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to
test_env.code_mem.back() = 0xE7FE; // b +#0
// Prepare test subjects
A32::UserConfig config{GetUserConfig(&test_env)};
A32Unicorn uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::Jit jit{config};
for (size_t run_number = 0; run_number < run_count; run_number++) {
ThumbTestEnv::RegisterArray initial_regs;
@ -211,7 +214,7 @@ void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
RunInstance(run_number, test_env, config, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
}
}
@ -225,7 +228,8 @@ void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to
// Prepare test subjects
A32Unicorn uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::UserConfig config{GetUserConfig(&test_env)};
A32::Jit jit{config};
for (size_t run_number = 0; run_number < run_count; run_number++) {
ThumbTestEnv::RegisterArray initial_regs;
@ -241,7 +245,7 @@ void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to
test_env.code_mem[i * 2 + 1] = first_halfword;
}
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
RunInstance(run_number, test_env, config, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
}
}
@ -504,7 +508,8 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thu
// Prepare test subjects
A32Unicorn<ThumbTestEnv> uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::UserConfig config{GetUserConfig(&test_env)};
A32::Jit jit{config};
constexpr ThumbTestEnv::RegisterArray initial_regs{
0xe90ecd70,
@ -534,5 +539,5 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thu
0xE7FE, // b +#0
};
RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
RunInstance(1, test_env, config, uni, jit, initial_regs, 5, 5);
}

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
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"

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
/* This file is part of the dynarmic project.
@ -10,8 +10,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/coprocessor.h"

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
/* This file is part of the dynarmic project.
@ -10,8 +10,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
using namespace Dynarmic;

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
/* This file is part of the dynarmic project.
@ -9,8 +9,8 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A32/a32.h"
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {

View file

@ -9,8 +9,8 @@
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/interface/optimization_flags.h"

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
/* This file is part of the dynarmic project.
@ -11,8 +11,8 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
using namespace Dynarmic;

View file

@ -15,11 +15,11 @@
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
#include "../fuzz_util.h"
#include "../rand_int.h"
#include "../unicorn_emu/a64_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"
@ -168,7 +168,7 @@ static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) {
return jit_user_config;
}
static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, Dynarmic::A64::UserConfig& conf, const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
jit_env.code_mem = instructions;
uni_env.code_mem = instructions;
jit_env.code_mem.emplace_back(0x14000000); // B .
@ -269,16 +269,13 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
fmt::print("\n");
const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {});
fmt::print("IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
const A64::LocationDescriptor location{instructions_start, FP::FPCR{fpcr}};
IR::Block ir_block{location};
A64::Translate(ir_block, location, get_code, {});
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
Optimization::Optimize(ir_block, conf, {});
fmt::print("Optimized IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n");
fmt::print("{}", jit.Disassemble());
fmt::print("Optimized IR:\n{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n{}", jit.Disassemble());
fmt::print("Interrupts:\n");
for (auto& i : uni_env.interrupts) {
puts(i.c_str());
@ -304,7 +301,8 @@ TEST_CASE("A64: Single random instruction", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -323,7 +321,7 @@ TEST_CASE("A64: Single random instruction", "[a64][unicorn]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -331,7 +329,8 @@ TEST_CASE("A64: Floating point instructions", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
static constexpr std::array<u64, 80> float_numbers{
@ -448,7 +447,7 @@ TEST_CASE("A64: Floating point instructions", "[a64][unicorn]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -456,7 +455,8 @@ TEST_CASE("A64: Small random block", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -483,7 +483,7 @@ TEST_CASE("A64: Small random block", "[a64][unicorn]") {
INFO("Instruction 4: 0x" << std::hex << instructions[3]);
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -491,7 +491,8 @@ TEST_CASE("A64: Large random block", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -512,6 +513,6 @@ TEST_CASE("A64: Large random block", "[a64][unicorn]") {
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}

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
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
TEST_CASE("misaligned load/store do not use page_table when detect_misaligned_access_via_page_table is set", "[a64]") {

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
using namespace Dynarmic;

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
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
using namespace Dynarmic;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
@ -7,9 +10,9 @@
#include <catch2/catch_test_macros.hpp>
#include "../rand_int.h"
#include "../unicorn_emu/a64_unicorn.h"
#include "./testenv.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/tests/A64/testenv.h"
using namespace Dynarmic;

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
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/op.h"

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
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/mantissa_util.h"
#include "dynarmic/common/safe_ops.h"

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
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/unpacked.h"

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
/* This file is part of the dynarmic project.
@ -6,7 +6,7 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./fuzz_util.h"
#include "dynarmic/tests/fuzz_util.h"
#include <cstring>
@ -14,7 +14,7 @@
#include <fmt/ostream.h>
#include "dynarmic/common/assert.h"
#include "./rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/rounding_mode.h"

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include <immintrin.h>
#include "../A64/testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/interface/exclusive_monitor.h"

View file

@ -22,8 +22,8 @@
#include "./A32/testenv.h"
#include "./A64/testenv.h"
#include "./fuzz_util.h"
#include "./rand_int.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"

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
/* This file is part of the dynarmic project.
@ -6,22 +6,14 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./a32_unicorn.h"
#include <type_traits>
#include "dynarmic/common/assert.h"
#include <fmt/format.h>
#include <mcl/bit/bit_field.hpp>
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/common/assert.h"
#include "dynarmic/tests/A32/testenv.h"
#include "../A32/testenv.h"
#define CHECKED(expr) \
do { \
if (auto cerr_ = (expr)) { \
ASSERT(false && "Call " #expr " failed with error: {} ({})\n", static_cast<size_t>(cerr_), \
uc_strerror(cerr_)); \
} \
} while (0)
#define CHECKED(expr) do if ((expr)) ASSERT(false && "Call " #expr " failed with error\n"); while (0)
constexpr u32 BEGIN_ADDRESS = 0;
constexpr u32 END_ADDRESS = ~u32(0);

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
/* This file is part of the dynarmic project.
@ -21,7 +21,7 @@
#include "dynarmic/common/common_types.h"
#include "../A32/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
namespace Unicorn::A32 {
static constexpr size_t num_gprs = 16;

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
/* This file is part of the dynarmic project.
@ -6,17 +6,11 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./a64_unicorn.h"
#include <fmt/format.h>
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/common/assert.h"
#define CHECKED(expr) \
do { \
if (auto cerr_ = (expr)) { \
ASSERT(false && "Call " #expr " failed with error: {} ({})\n", static_cast<size_t>(cerr_), \
uc_strerror(cerr_)); \
} \
} while (0)
#define CHECKED(expr) do if ((expr)) ASSERT(false && "Call " #expr " failed with error\n"); while (0)
constexpr u64 BEGIN_ADDRESS = 0;
constexpr u64 END_ADDRESS = ~u64(0);
@ -172,7 +166,7 @@ void A64Unicorn::DumpMemoryInformation() {
void A64Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) {
auto* this_ = static_cast<A64Unicorn*>(user_data);
u32 esr;
u32 esr = 0;
//CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR_EL0, &esr));
auto ec = esr >> 26;

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
/* This file is part of the dynarmic project.
@ -21,7 +21,7 @@
#include "dynarmic/common/common_types.h"
#include "../A64/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
class A64Unicorn final {
public:

View file

@ -44,7 +44,6 @@ add_library(video_core STATIC
engines/sw_blitter/converter.h
engines/const_buffer_info.h
engines/draw_manager.cpp
engines/draw_manager.h
engines/engine_interface.h
engines/engine_upload.cpp
engines/engine_upload.h

View file

@ -356,7 +356,7 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
if (is_indexed) {
BindHostIndexBuffer();
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
@ -740,30 +740,25 @@ void BufferCache<P>::BindHostIndexBuffer() {
TouchBuffer(buffer, channel_state->index_buffer.buffer_id);
const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr);
const u32 size = channel_state->index_buffer.size;
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
const auto& draw_state = maxwell3d->draw_manager.draw_state;
if (draw_state.inline_index_draw_indexes.empty()) {
SynchronizeBuffer(buffer, channel_state->index_buffer.device_addr, size);
} else {
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);
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);
}
} else {
SynchronizeBuffer(buffer, channel_state->index_buffer.device_addr, size);
}
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const u32 new_offset =
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
const u32 new_offset = offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
runtime.BindIndexBuffer(buffer, new_offset, size);
} else {
buffer.MarkUsage(offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count,
buffer, offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, draw_state.index_buffer.first, draw_state.index_buffer.count, buffer, offset, size);
}
}
@ -945,10 +940,9 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
return alignment > 1 && (offset % alignment) != 0;
}
}();
const bool use_fast_buffer = needs_alignment_stream ||
(has_host_buffer &&
size <= channel_state->uniform_buffer_skip_cache_size &&
!memory_tracker.IsRegionGpuModified(device_addr, size));
const bool use_fast_buffer = needs_alignment_stream
|| (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) {
if (runtime.HasFastBufferSubData()) {
@ -1226,7 +1220,7 @@ template <class P>
void BufferCache<P>::UpdateIndexBuffer() {
// We have to check for the dirty flags and index count
// The index count is currently changed without updating the dirty flags
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
const auto& index_buffer_ref = draw_state.index_buffer;
auto& flags = maxwell3d->dirty.flags;
if (!flags[Dirty::IndexBuffer]) {

View file

@ -32,7 +32,7 @@
#include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@ -305,7 +305,7 @@ public:
[[nodiscard]] bool IsRegionCpuModified(DAddr addr, size_t size);
void SetDrawIndirect(
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect_) {
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect_) {
current_draw_indirect = current_draw_indirect_;
}
@ -480,7 +480,7 @@ private:
#endif
DelayedDestructionRing<Buffer, TICKS_TO_DESTROY> delayed_destruction_ring;
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
u32 last_index_count = 0;

View file

@ -1,23 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra::Engines {
DrawManager::DrawManager(Maxwell3D* maxwell3d_) : maxwell3d(maxwell3d_) {}
void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
const auto& regs{maxwell3d->regs};
void Maxwell3D::DrawManager::ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument) {
switch (method) {
case MAXWELL3D_REG_INDEX(clear_surface):
return Clear(1);
return Clear(maxwell3d, 1);
case MAXWELL3D_REG_INDEX(draw.begin):
return DrawBegin();
return DrawBegin(maxwell3d);
case MAXWELL3D_REG_INDEX(draw.end):
return DrawEnd();
return DrawEnd(maxwell3d);
case MAXWELL3D_REG_INDEX(vertex_buffer.first):
case MAXWELL3D_REG_INDEX(vertex_buffer.count):
case MAXWELL3D_REG_INDEX(index_buffer.first):
@ -33,33 +34,29 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
case MAXWELL3D_REG_INDEX(index_buffer32_first):
case MAXWELL3D_REG_INDEX(index_buffer16_first):
case MAXWELL3D_REG_INDEX(index_buffer8_first):
return DrawIndexSmall(argument);
return DrawIndexSmall(maxwell3d, argument);
case MAXWELL3D_REG_INDEX(draw_inline_index):
SetInlineIndexBuffer(argument);
SetInlineIndexBuffer(maxwell3d, argument);
break;
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
SetInlineIndexBuffer(regs.inline_index_2x16.even);
SetInlineIndexBuffer(regs.inline_index_2x16.odd);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_2x16.even);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_2x16.odd);
break;
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
SetInlineIndexBuffer(regs.inline_index_4x8.index0);
SetInlineIndexBuffer(regs.inline_index_4x8.index1);
SetInlineIndexBuffer(regs.inline_index_4x8.index2);
SetInlineIndexBuffer(regs.inline_index_4x8.index3);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_4x8.index0);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_4x8.index1);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_4x8.index2);
SetInlineIndexBuffer(maxwell3d, maxwell3d.regs.inline_index_4x8.index3);
break;
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(),
regs.vertex_array_instance_first.start.Value(),
regs.vertex_array_instance_first.count.Value(), false);
DrawArrayInstanced(maxwell3d, maxwell3d.regs.vertex_array_instance_first.topology.Value(), maxwell3d.regs.vertex_array_instance_first.start.Value(), maxwell3d.regs.vertex_array_instance_first.count.Value(), false);
break;
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(),
regs.vertex_array_instance_subsequent.start.Value(),
regs.vertex_array_instance_subsequent.count.Value(), true);
DrawArrayInstanced(maxwell3d, maxwell3d.regs.vertex_array_instance_subsequent.topology.Value(), maxwell3d.regs.vertex_array_instance_subsequent.start.Value(), maxwell3d.regs.vertex_array_instance_subsequent.count.Value(), true);
break;
}
case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
DrawTexture();
DrawTexture(maxwell3d);
break;
}
default:
@ -67,101 +64,87 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
}
}
void DrawManager::Clear(u32 layer_count) {
if (maxwell3d->ShouldExecute()) {
maxwell3d->rasterizer->Clear(layer_count);
void Maxwell3D::DrawManager::Clear(Maxwell3D& maxwell3d, u32 layer_count) {
if (maxwell3d.ShouldExecute()) {
maxwell3d.rasterizer->Clear(layer_count);
}
}
void DrawManager::DrawDeferred() {
void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) {
if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) {
return;
}
DrawEnd(draw_state.instance_count + 1, true);
DrawEnd(maxwell3d, draw_state.instance_count + 1, true);
draw_state.instance_count = 0;
}
void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
u32 base_instance, u32 num_instances) {
void Maxwell3D::DrawManager::DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances) {
draw_state.topology = topology;
draw_state.vertex_buffer.first = vertex_first;
draw_state.vertex_buffer.count = vertex_count;
draw_state.base_instance = base_instance;
ProcessDraw(false, num_instances);
ProcessDraw(maxwell3d, false, num_instances);
}
void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
bool subsequent) {
void Maxwell3D::DrawManager::DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent) {
draw_state.topology = topology;
draw_state.vertex_buffer.first = vertex_first;
draw_state.vertex_buffer.count = vertex_count;
if (!subsequent) {
draw_state.instance_count = 1;
}
draw_state.base_instance = draw_state.instance_count - 1;
draw_state.draw_mode = DrawMode::Instance;
draw_state.instance_count++;
ProcessDraw(false, 1);
ProcessDraw(maxwell3d, false, 1);
}
void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
u32 base_index, u32 base_instance, u32 num_instances) {
const auto& regs{maxwell3d->regs};
void Maxwell3D::DrawManager::DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances) {
draw_state.topology = topology;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer = maxwell3d.regs.index_buffer;
draw_state.index_buffer.first = index_first;
draw_state.index_buffer.count = index_count;
draw_state.base_index = base_index;
draw_state.base_instance = base_instance;
ProcessDraw(true, num_instances);
ProcessDraw(maxwell3d, true, num_instances);
}
void DrawManager::DrawArrayIndirect(PrimitiveTopology topology) {
void Maxwell3D::DrawManager::DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology) {
draw_state.topology = topology;
ProcessDrawIndirect();
ProcessDrawIndirect(maxwell3d);
}
void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first,
u32 index_count) {
const auto& regs{maxwell3d->regs};
void Maxwell3D::DrawManager::DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count) {
draw_state.topology = topology;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer = maxwell3d.regs.index_buffer;
draw_state.index_buffer.first = index_first;
draw_state.index_buffer.count = index_count;
ProcessDrawIndirect();
ProcessDrawIndirect(maxwell3d);
}
void DrawManager::SetInlineIndexBuffer(u32 index) {
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
void Maxwell3D::DrawManager::SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 index) {
draw_state.inline_index_draw_indexes.push_back(u8(index & 0x000000ff));
draw_state.inline_index_draw_indexes.push_back(u8((index & 0x0000ff00) >> 8));
draw_state.inline_index_draw_indexes.push_back(u8((index & 0x00ff0000) >> 16));
draw_state.inline_index_draw_indexes.push_back(u8((index & 0xff000000) >> 24));
draw_state.draw_mode = DrawMode::InlineIndex;
}
void DrawManager::DrawBegin() {
const auto& regs{maxwell3d->regs};
auto reset_instance_count = regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First;
auto increment_instance_count =
regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent;
void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) {
auto reset_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First;
auto increment_instance_count = maxwell3d.regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent;
if (reset_instance_count) {
DrawDeferred();
DrawDeferred(maxwell3d);
draw_state.instance_count = 0;
draw_state.draw_mode = DrawMode::General;
} else if (increment_instance_count) {
draw_state.instance_count++;
draw_state.draw_mode = DrawMode::Instance;
}
draw_state.topology = regs.draw.topology;
draw_state.topology = maxwell3d.regs.draw.topology;
}
void DrawManager::DrawEnd(u32 instance_count, bool force_draw) {
const auto& regs{maxwell3d->regs};
void Maxwell3D::DrawManager::DrawEnd(Maxwell3D& maxwell3d, u32 instance_count, bool force_draw) {
switch (draw_state.draw_mode) {
case DrawMode::Instance:
if (!force_draw) {
@ -169,119 +152,100 @@ void DrawManager::DrawEnd(u32 instance_count, bool force_draw) {
}
[[fallthrough]];
case DrawMode::General:
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
draw_state.base_instance = maxwell3d.regs.global_base_instance_index;
draw_state.base_index = maxwell3d.regs.global_base_vertex_index;
if (draw_state.draw_indexed) {
draw_state.index_buffer = regs.index_buffer;
ProcessDraw(true, instance_count);
draw_state.index_buffer = maxwell3d.regs.index_buffer;
ProcessDraw(maxwell3d, true, instance_count);
} else {
draw_state.vertex_buffer = regs.vertex_buffer;
ProcessDraw(false, instance_count);
draw_state.vertex_buffer = maxwell3d.regs.vertex_buffer;
ProcessDraw(maxwell3d, false, instance_count);
}
draw_state.draw_indexed = false;
break;
case DrawMode::InlineIndex:
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer.count =
static_cast<u32>(draw_state.inline_index_draw_indexes.size() / 4);
draw_state.base_instance = maxwell3d.regs.global_base_instance_index;
draw_state.base_index = maxwell3d.regs.global_base_vertex_index;
draw_state.index_buffer = maxwell3d.regs.index_buffer;
draw_state.index_buffer.count = u32(draw_state.inline_index_draw_indexes.size() / 4);
draw_state.index_buffer.format = Maxwell3D::Regs::IndexFormat::UnsignedInt;
maxwell3d->dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
ProcessDraw(true, instance_count);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
ProcessDraw(maxwell3d, true, instance_count);
draw_state.inline_index_draw_indexes.clear();
break;
}
}
void DrawManager::DrawIndexSmall(u32 argument) {
const auto& regs{maxwell3d->regs};
IndexBufferSmall index_small_params{argument};
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
draw_state.index_buffer = regs.index_buffer;
void Maxwell3D::DrawManager::DrawIndexSmall(Maxwell3D& maxwell3d, u32 argument) {
Maxwell3D::Regs::IndexBufferSmall index_small_params{argument};
draw_state.base_instance = maxwell3d.regs.global_base_instance_index;
draw_state.base_index = maxwell3d.regs.global_base_vertex_index;
draw_state.index_buffer = maxwell3d.regs.index_buffer;
draw_state.index_buffer.first = index_small_params.first;
draw_state.index_buffer.count = index_small_params.count;
draw_state.topology = index_small_params.topology;
maxwell3d->dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
ProcessDraw(true, 1);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
ProcessDraw(maxwell3d, true, 1);
}
void DrawManager::DrawTexture() {
const auto& regs{maxwell3d->regs};
draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f;
draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f;
const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f;
const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f;
const bool lower_left{regs.window_origin.mode !=
Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
void Maxwell3D::DrawManager::DrawTexture(Maxwell3D& maxwell3d) {
draw_texture_state.dst_x0 = f32(maxwell3d.regs.draw_texture.dst_x0) / 4096.f;
draw_texture_state.dst_y0 = f32(maxwell3d.regs.draw_texture.dst_y0) / 4096.f;
const auto dst_width = f32(maxwell3d.regs.draw_texture.dst_width) / 4096.f;
const auto dst_height = f32(maxwell3d.regs.draw_texture.dst_height) / 4096.f;
const bool lower_left{maxwell3d.regs.window_origin.mode != Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
if (lower_left) {
draw_texture_state.dst_y0 =
static_cast<f32>(regs.surface_clip.height) - draw_texture_state.dst_y0;
draw_texture_state.dst_y0 = f32(maxwell3d.regs.surface_clip.height) - draw_texture_state.dst_y0;
}
draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width;
draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height;
draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
draw_texture_state.src_x1 =
(static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width +
draw_texture_state.src_x0;
draw_texture_state.src_y1 =
(static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height +
draw_texture_state.src_y0;
draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
draw_texture_state.src_texture = regs.draw_texture.src_texture;
maxwell3d->rasterizer->DrawTexture();
draw_texture_state.src_x0 = f32(maxwell3d.regs.draw_texture.src_x0) / 4096.f;
draw_texture_state.src_y0 = f32(maxwell3d.regs.draw_texture.src_y0) / 4096.f;
draw_texture_state.src_x1 = (f32(maxwell3d.regs.draw_texture.dx_du) / 4294967296.f) * dst_width + draw_texture_state.src_x0;
draw_texture_state.src_y1 = (f32(maxwell3d.regs.draw_texture.dy_dv) / 4294967296.f) * dst_height + draw_texture_state.src_y0;
draw_texture_state.src_sampler = maxwell3d.regs.draw_texture.src_sampler;
draw_texture_state.src_texture = maxwell3d.regs.draw_texture.src_texture;
maxwell3d.rasterizer->DrawTexture();
}
void DrawManager::UpdateTopology() {
const auto& regs{maxwell3d->regs};
switch (regs.primitive_topology_control) {
case PrimitiveTopologyControl::UseInBeginMethods:
void Maxwell3D::DrawManager::UpdateTopology(Maxwell3D& maxwell3d) {
switch (maxwell3d.regs.primitive_topology_control) {
case Maxwell3D::Regs::PrimitiveTopologyControl::UseInBeginMethods:
break;
case PrimitiveTopologyControl::UseSeparateState:
switch (regs.topology_override) {
case PrimitiveTopologyOverride::None:
case Maxwell3D::Regs::PrimitiveTopologyControl::UseSeparateState:
switch (maxwell3d.regs.topology_override) {
case Maxwell3D::Regs::PrimitiveTopologyOverride::None:
break;
case PrimitiveTopologyOverride::Points:
draw_state.topology = PrimitiveTopology::Points;
case Maxwell3D::Regs::PrimitiveTopologyOverride::Points:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Points;
break;
case PrimitiveTopologyOverride::Lines:
draw_state.topology = PrimitiveTopology::Lines;
case Maxwell3D::Regs::PrimitiveTopologyOverride::Lines:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Lines;
break;
case PrimitiveTopologyOverride::LineStrip:
draw_state.topology = PrimitiveTopology::LineStrip;
case Maxwell3D::Regs::PrimitiveTopologyOverride::LineStrip:
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
break;
default:
draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override);
draw_state.topology = Maxwell3D::Regs::PrimitiveTopology(maxwell3d.regs.topology_override);
break;
}
break;
}
}
void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology,
draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
UpdateTopology();
if (maxwell3d->ShouldExecute()) {
maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
void Maxwell3D::DrawManager::ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology, draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
UpdateTopology(maxwell3d);
if (maxwell3d.ShouldExecute()) {
maxwell3d.rasterizer->Draw(draw_indexed, instance_count);
}
}
void DrawManager::ProcessDrawIndirect() {
LOG_TRACE(
HW_GPU,
"called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}",
draw_state.topology, indirect_state.is_indexed, indirect_state.include_count,
indirect_state.buffer_size, indirect_state.max_draw_counts);
UpdateTopology();
if (maxwell3d->ShouldExecute()) {
maxwell3d->rasterizer->DrawIndirect();
void Maxwell3D::DrawManager::ProcessDrawIndirect(Maxwell3D& maxwell3d) {
LOG_TRACE(HW_GPU, "called, topology={}, is_indexed={}, includes_count={}, buffer_size={}, max_draw_count={}", draw_state.topology, indirect_state.is_indexed, indirect_state.include_count, indirect_state.buffer_size, indirect_state.max_draw_counts);
UpdateTopology(maxwell3d);
if (maxwell3d.ShouldExecute()) {
maxwell3d.rasterizer->DrawIndirect();
}
}
} // namespace Tegra::Engines

View file

@ -1,117 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
using PrimitiveTopologyControl = Maxwell3D::Regs::PrimitiveTopologyControl;
using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
using IndexBuffer = Maxwell3D::Regs::IndexBuffer;
using VertexBuffer = Maxwell3D::Regs::VertexBuffer;
using IndexBufferSmall = Maxwell3D::Regs::IndexBufferSmall;
class DrawManager {
public:
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
struct State {
PrimitiveTopology topology{};
DrawMode draw_mode{};
bool draw_indexed{};
u32 base_index{};
VertexBuffer vertex_buffer;
IndexBuffer index_buffer;
u32 base_instance{};
u32 instance_count{};
std::vector<u8> inline_index_draw_indexes;
};
struct DrawTextureState {
f32 dst_x0;
f32 dst_y0;
f32 dst_x1;
f32 dst_y1;
f32 src_x0;
f32 src_y0;
f32 src_x1;
f32 src_y1;
u32 src_sampler;
u32 src_texture;
};
struct IndirectParams {
bool is_byte_count;
bool is_indexed;
bool include_count;
GPUVAddr count_start_address;
GPUVAddr indirect_start_address;
size_t buffer_size;
size_t max_draw_counts;
size_t stride;
};
explicit DrawManager(Maxwell3D* maxwell_3d);
void ProcessMethodCall(u32 method, u32 argument);
void Clear(u32 layer_count);
void DrawDeferred();
void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
u32 base_instance, u32 num_instances);
void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
bool subsequent);
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
u32 base_instance, u32 num_instances);
void DrawArrayIndirect(PrimitiveTopology topology);
void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
const State& GetDrawState() const {
return draw_state;
}
const DrawTextureState& GetDrawTextureState() const {
return draw_texture_state;
}
IndirectParams& GetIndirectParams() {
return indirect_state;
}
const IndirectParams& GetIndirectParams() const {
return indirect_state;
}
private:
void SetInlineIndexBuffer(u32 index);
void DrawBegin();
void DrawEnd(u32 instance_count = 1, bool force_draw = false);
void DrawIndexSmall(u32 argument);
void DrawTexture();
void UpdateTopology();
void ProcessDraw(bool draw_indexed, u32 instance_count);
void ProcessDrawIndirect();
Maxwell3D* maxwell3d{};
State draw_state{};
DrawTextureState draw_texture_state{};
IndirectParams indirect_state{};
};
} // namespace Tegra::Engines

View file

@ -13,7 +13,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@ -26,7 +26,8 @@ namespace Tegra::Engines {
constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
: draw_manager{std::make_unique<DrawManager>(this)}, system{system_}
: draw_manager()
, system{system_}
, memory_manager{memory_manager_}
#ifdef ARCHITECTURE_x86_64
, macro_engine(bool(Settings::values.disable_macro_jit))
@ -373,8 +374,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
case MAXWELL3D_REG_INDEX(sync_info):
return ProcessSyncPoint();
case MAXWELL3D_REG_INDEX(launch_dma):
return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() ==
Regs::LaunchDMA::Layout::Pitch);
return upload_state.ProcessExec(regs.launch_dma.memory_layout.Value() == Regs::LaunchDMA::Layout::Pitch);
case MAXWELL3D_REG_INDEX(inline_data):
upload_state.ProcessData(argument, is_last_call);
return;
@ -386,7 +386,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return rasterizer->TiledCacheBarrier();
default:
draw_manager->ProcessMethodCall(method, argument);
draw_manager.ProcessMethodCall(*this, method, argument);
break;
}
}
@ -401,8 +401,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
// Execute the current macro.
macro_engine.Execute(*this, macro_positions[entry], parameters);
draw_manager->DrawDeferred();
draw_manager.DrawDeferred(*this);
}
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {

View file

@ -25,6 +25,7 @@
#include "video_core/gpu.h"
#include "video_core/macro.h"
#include "video_core/textures/texture.h"
#include "video_core/engines/maxwell_3d.h"
namespace Core {
class System;
@ -40,8 +41,6 @@ class RasterizerInterface;
namespace Tegra::Engines {
class DrawManager;
/**
* This Engine is known as GF100_3D. Documentation can be found in:
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/3d/clb197.h
@ -543,7 +542,7 @@ public:
}
GPUVAddr StorageLimitAddress() const {
return (GPUVAddr{storage_limit_address_high} << 32) |
GPUVAddr{storage_limit_address_low};
GPUVAddr{storage_limit_address_low};
}
};
@ -819,7 +818,7 @@ public:
u32 Map(std::size_t index) const {
const std::array<u32, NumRenderTargets> maps{target0, target1, target2, target3,
target4, target5, target6, target7};
target4, target5, target6, target7};
ASSERT(index < maps.size());
return maps[index];
}
@ -1831,7 +1830,7 @@ public:
bool AnyEnabled() const {
return output0_enable || output1_enable || output2_enable || output3_enable ||
output4_enable || output5_enable || output6_enable || output7_enable;
output4_enable || output5_enable || output6_enable || output7_enable;
}
};
@ -1870,7 +1869,7 @@ public:
bool AnyEnabled() const {
return plane0 || plane1 || plane2 || plane3 || plane4 || plane5 || plane6 ||
plane7;
plane7;
}
};
@ -3023,8 +3022,7 @@ public:
u32 bindless_texture_const_buffer_slot; ///< 0x2608
u32 trap_handler; ///< 0x260C
INSERT_PADDING_BYTES_NOINIT(0x1F0);
std::array<std::array<StreamOutLayout, 32>, NumTransformFeedbackBuffers>
stream_out_layout; ///< 0x2800
std::array<std::array<StreamOutLayout, 32>, NumTransformFeedbackBuffers> stream_out_layout; ///< 0x2800
INSERT_PADDING_BYTES_NOINIT(0x93C);
ShaderPerformance shader_performance; ///< 0x333C
INSERT_PADDING_BYTES_NOINIT(0x18);
@ -3035,6 +3033,62 @@ public:
};
// clang-format on
struct DrawManager {
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
struct State {
Maxwell3D::Regs::PrimitiveTopology topology{};
DrawMode draw_mode{};
bool draw_indexed{};
u32 base_index{};
Maxwell3D::Regs::VertexBuffer vertex_buffer;
Maxwell3D::Regs::IndexBuffer index_buffer;
u32 base_instance{};
u32 instance_count{};
std::vector<u8> inline_index_draw_indexes;
};
struct DrawTextureState {
f32 dst_x0;
f32 dst_y0;
f32 dst_x1;
f32 dst_y1;
f32 src_x0;
f32 src_y0;
f32 src_x1;
f32 src_y1;
u32 src_sampler;
u32 src_texture;
};
struct IndirectParams {
bool is_byte_count;
bool is_indexed;
bool include_count;
GPUVAddr count_start_address;
GPUVAddr indirect_start_address;
size_t buffer_size;
size_t max_draw_counts;
size_t stride;
};
void ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument);
void Clear(Maxwell3D& maxwell3d, u32 layer_count);
void DrawDeferred(Maxwell3D& maxwell3d);
void DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances);
void DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent);
void DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances);
void DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology);
void DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count);
void SetInlineIndexBuffer(Maxwell3D& maxwell3d, u32 index);
void DrawBegin(Maxwell3D& maxwell3d);
void DrawEnd(Maxwell3D& maxwell3d, u32 instance_count = 1, bool force_draw = false);
void DrawIndexSmall(Maxwell3D& maxwell3d, u32 argument);
void DrawTexture(Maxwell3D& maxwell3d);
void UpdateTopology(Maxwell3D& maxwell3d);
void ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count);
void ProcessDrawIndirect(Maxwell3D& maxwell3d);
State draw_state{};
DrawTextureState draw_texture_state{};
IndirectParams indirect_state{};
};
Regs regs{};
/// Store temporary hw register values, used by some calls to restore state after a operation
@ -3102,8 +3156,7 @@ public:
Tables tables{};
} dirty;
std::unique_ptr<DrawManager> draw_manager;
friend class DrawManager;
DrawManager draw_manager;
GPUVAddr GetMacroAddress(size_t index) const {
return macro_addresses[index];

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 2019 yuzu Emulator Project
@ -63,9 +63,7 @@ ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
ThreadManager::~ThreadManager() = default;
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
Tegra::Control::Scheduler& scheduler) {
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, Tegra::Control::Scheduler& scheduler) {
rasterizer = renderer.ReadRasterizer();
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(scheduler), std::ref(state));

View file

@ -31,7 +31,7 @@
#include "common/settings.h"
#include "common/container_hash.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/dirty_flags.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/macro.h"
@ -83,7 +83,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
return;
}
auto& params = maxwell3d.draw_manager->GetIndirectParams();
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = false;
params.is_indexed = false;
params.include_count = false;
@ -98,7 +98,7 @@ void HLE_DrawArraysIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<co
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
}
maxwell3d.draw_manager->DrawArrayIndirect(topology);
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
if (extended) {
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
@ -127,7 +127,7 @@ void HLE_DrawArraysIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<c
maxwell3d.engine_state = Maxwell3D::EngineHint::OnHLEMacro;
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
}
maxwell3d.draw_manager->DrawArray(topology, vertex_first, vertex_count, base_instance, instance_count);
maxwell3d.draw_manager.DrawArray(maxwell3d, topology, vertex_first, vertex_count, base_instance, instance_count);
if (extended) {
maxwell3d.regs.global_base_instance_index = 0;
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
@ -154,7 +154,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
}
auto& params = maxwell3d.draw_manager->GetIndirectParams();
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = false;
params.is_indexed = true;
params.include_count = false;
@ -164,7 +164,7 @@ void HLE_DrawIndexedIndirect::Execute(Engines::Maxwell3D& maxwell3d, std::span<c
params.max_draw_counts = 1;
params.stride = 0;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
@ -187,7 +187,7 @@ void HLE_DrawIndexedIndirect::Fallback(Engines::Maxwell3D& maxwell3d, std::span<
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
}
maxwell3d.draw_manager->DrawIndex(Tegra::Maxwell3D::Regs::PrimitiveTopology(parameters[0]), parameters[3], parameters[1], element_base, base_instance, instance_count);
maxwell3d.draw_manager.DrawIndex(maxwell3d, Tegra::Maxwell3D::Regs::PrimitiveTopology(parameters[0]), parameters[3], parameters[1], element_base, base_instance, instance_count);
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
@ -206,7 +206,7 @@ void HLE_MultiLayerClear::Execute(Engines::Maxwell3D& maxwell3d, std::span<const
ASSERT(clear_params.layer == 0);
maxwell3d.regs.clear_surface.raw = clear_params.raw;
maxwell3d.draw_manager->Clear(num_layers);
maxwell3d.draw_manager.Clear(maxwell3d, num_layers);
}
void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[2]);
@ -230,7 +230,7 @@ void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, s
const std::size_t draw_count = end_indirect - start_indirect;
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
auto& params = maxwell3d.draw_manager->GetIndirectParams();
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = false;
params.is_indexed = true;
params.include_count = true;
@ -244,7 +244,7 @@ void HLE_MultiDrawIndexedIndirectCount::Execute(Engines::Maxwell3D& maxwell3d, s
maxwell3d.SetHLEReplacementAttributeType(0, 0x640, Maxwell3D::HLEReplacementAttributeType::BaseVertex);
maxwell3d.SetHLEReplacementAttributeType(0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
maxwell3d.SetHLEReplacementAttributeType(0, 0x648, Maxwell3D::HLEReplacementAttributeType::DrawID);
maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, estimate);
maxwell3d.draw_manager.DrawIndexedIndirect(maxwell3d, topology, 0, estimate);
maxwell3d.engine_state = Maxwell3D::EngineHint::None;
maxwell3d.replace_table.clear();
}
@ -280,7 +280,7 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
maxwell3d.CallMethod(0x8e3, 0x648, true);
maxwell3d.CallMethod(0x8e4, static_cast<u32>(index), true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.draw_manager->DrawIndex(topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
maxwell3d.draw_manager.DrawIndex(maxwell3d, topology, parameters[base + 2], parameters[base], base_vertex, base_instance, parameters[base + 1]);
}
}
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
@ -290,7 +290,7 @@ void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span
Fallback(maxwell3d, parameters);
return;
}
auto& params = maxwell3d.draw_manager->GetIndirectParams();
auto& params = maxwell3d.draw_manager.indirect_state;
params.is_byte_count = true;
params.is_indexed = false;
params.include_count = false;
@ -302,18 +302,14 @@ void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager->DrawArrayIndirect(topology);
maxwell3d.draw_manager.DrawArrayIndirect(maxwell3d, topology);
}
void HLE_DrawIndirectByteCount::Fallback(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters) {
maxwell3d.RefreshParameters();
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager->DrawArray(
maxwell3d.regs.draw.topology, 0,
maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
maxwell3d.draw_manager.DrawArray(maxwell3d, maxwell3d.regs.draw.topology, 0, maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
}
void HLE_C713C83D8F63CCF3::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
maxwell3d.RefreshParameters();

View file

@ -245,7 +245,7 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
SyncState();
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
BeginTransformFeedback(pipeline, primitive_mode);
@ -260,12 +260,12 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance);
const GLsizei num_instances = static_cast<GLsizei>(instance_count);
const auto& draw_state = maxwell3d->draw_manager.draw_state;
const GLuint base_instance = GLuint(draw_state.base_instance);
const GLsizei num_instances = GLsizei(instance_count);
if (is_indexed) {
const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
const GLint base_vertex = GLint(draw_state.base_index);
const GLsizei num_vertices = GLsizei(draw_state.index_buffer.count);
const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
@ -302,7 +302,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
}
void RasterizerOpenGL::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
const auto& params = maxwell3d->draw_manager.indirect_state;
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
if (params.is_byte_count) {
@ -358,12 +358,12 @@ void RasterizerOpenGL::DrawTexture() {
SyncState();
const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state;
const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
const auto Scale = [&](auto dim) -> s32 {
return Settings::values.resolution_info.ScaleUp(static_cast<s32>(dim));
return Settings::values.resolution_info.ScaleUp(s32(dim));
};
Region2D dst_region = {

View file

@ -25,7 +25,7 @@
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/profile.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@ -362,7 +362,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
const auto& regs{maxwell3d->regs};
graphics_key.raw = 0;
graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
graphics_key.gs_input_topology.Assign(maxwell3d->draw_manager->GetDrawState().topology);
graphics_key.gs_input_topology.Assign(maxwell3d->draw_manager.draw_state.topology);
graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
graphics_key.tessellation_clockwise.Assign(
@ -402,7 +402,7 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n
// If games are using a small index count, we can assume these are full screen quads.
// Usually these shaders are only used once for building textures so we can assume they
// can't be built async
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) {
return pipeline;
}

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 2019 yuzu Emulator Project
@ -12,7 +12,7 @@
#include "common/cityhash.h"
#include "common/common_types.h"
#include "common/settings.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
@ -54,7 +54,7 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) {
const Maxwell& regs = maxwell3d.regs;
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
const auto topology_ = maxwell3d.draw_manager.draw_state.topology;
raw1 = 0;
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);

View file

@ -675,7 +675,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
// If games are using a small index count, we can assume these are full screen quads.
// Usually these shaders are only used once for building textures so we can assume they
// can't be built async
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) {
return pipeline;
}

View file

@ -16,7 +16,7 @@
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/query_cache/query_cache.h"
#include "video_core/rasterizer_interface.h"
@ -902,7 +902,7 @@ private:
streams_mask = 0; // reset previously recorded streams
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0;
out_topology = maxwell3d.draw_manager->GetDrawState().topology;
out_topology = maxwell3d.draw_manager.draw_state.topology;
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {

View file

@ -20,7 +20,7 @@
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/gpu_logging/gpu_logging.h"
#include "video_core/control/channel_state.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
@ -46,7 +46,6 @@
namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using MaxwellDrawState = Tegra::Engines::DrawManager::State;
using VideoCommon::ImageViewId;
using VideoCommon::ImageViewType;
@ -151,7 +150,7 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
return scissor;
}
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) {
DrawParams MakeDrawParams(const Tegra::Engines::Maxwell3D::DrawManager::State& draw_state, u32 num_instances, bool is_indexed) {
DrawParams params{
.base_instance = draw_state.base_instance,
.num_instances = num_instances,
@ -231,15 +230,13 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates();
HandleTransformFeedback();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
draw_func();
}
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& draw_state = maxwell3d->draw_manager.draw_state;
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
@ -298,7 +295,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
}
void RasterizerVulkan::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
const auto& params = maxwell3d->draw_manager.indirect_state;
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params] {
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
@ -368,9 +365,8 @@ void RasterizerVulkan::DrawTexture() {
UpdateDynamicStates();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state;
const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
const auto* framebuffer = texture_cache.GetFramebuffer();
@ -1530,10 +1526,9 @@ void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& re
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable,
};
const u32 topology_index = static_cast<u32>(maxwell3d->draw_manager->GetDrawState().topology);
const u32 topology_index = u32(maxwell3d->draw_manager.draw_state.topology);
const u32 enable = enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]];
scheduler.Record(
[enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); });
}
void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) {

View file

@ -9,6 +9,10 @@
#include <limits>
#include <vector>
#ifdef __ANDROID__
#include <android/api-level.h>
#endif
#include "common/logging.h"
#include "common/settings.h"
#include "common/settings_enums.h"
@ -170,6 +174,7 @@ bool Swapchain::AcquireNextImage() {
break;
}
const auto wait_with_frame_pacing = [this] {
switch (Settings::values.frame_pacing_mode.GetValue()) {
case Settings::FramePacingMode::Target_Auto:
scheduler.Wait(resource_ticks[image_index]);
@ -187,6 +192,17 @@ bool Swapchain::AcquireNextImage() {
scheduler.Wait(resource_ticks[image_index], 120.0);
break;
}
};
#ifdef __ANDROID__
if (android_get_device_api_level() >= 30) {
scheduler.Wait(resource_ticks[image_index]);
} else {
wait_with_frame_pacing();
}
#else
wait_with_frame_pacing();
#endif
resource_ticks[image_index] = scheduler.CurrentTick();