Compare commits
97 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ff3a232b | ||
|
|
eabe45346f | ||
|
|
701ca96827 | ||
|
|
26211ac339 | ||
|
|
3e53d8138c | ||
|
|
ddca512f3f | ||
|
|
e16d1b85f1 | ||
|
|
2572b0a5ea | ||
|
|
e8cbc3b4c5 | ||
|
|
64965cc658 | ||
|
|
20b62dbd30 | ||
|
|
9d081a8729 | ||
|
|
826e0785bf | ||
|
|
3b582d5fb2 | ||
|
|
7356ab1de6 | ||
|
|
daf2ef8f1c | ||
|
|
5fbd6954ef | ||
|
|
957840be91 | ||
|
|
852de7a771 | ||
|
|
ca154d466a | ||
|
|
97f273e94e | ||
|
|
59335f6796 | ||
|
|
c31f19b6d1 | ||
|
|
9abceaed61 | ||
|
|
cdb2e4eaff | ||
|
|
168c9ee341 | ||
|
|
8d99aae45b | ||
|
|
f047f376d4 | ||
|
|
84d4da89a5 | ||
|
|
1d57851fc7 | ||
|
|
3027917f39 | ||
|
|
7e0f70e5a1 | ||
|
|
aab68674c0 | ||
|
|
7f1c6def1f | ||
|
|
db7bcd51ae | ||
|
|
eb0713f781 | ||
|
|
57a05b1653 | ||
|
|
69c92b8156 | ||
|
|
4acbf3a193 | ||
|
|
54d6273975 | ||
|
|
98a6cd02c8 | ||
|
|
52acdafa95 | ||
|
|
975e17aa13 | ||
|
|
72118935a1 | ||
|
|
109c31c90f | ||
|
|
344e171cc7 | ||
|
|
bcbc25eeb3 | ||
|
|
b0365a81c2 | ||
|
|
327d225c3e | ||
|
|
aa075a0c08 | ||
|
|
38c48cf8d8 | ||
|
|
4975f60162 | ||
|
|
0d033e6b45 | ||
|
|
9c67334031 | ||
|
|
1fb33bd1e1 | ||
|
|
405d685101 | ||
|
|
e5a446a0df | ||
|
|
0e61d711e2 | ||
|
|
bc95753107 | ||
|
|
60e0d4a177 | ||
|
|
4c42655a2d | ||
|
|
ece0c1095d | ||
|
|
f426fd95fe | ||
|
|
fa660190ff | ||
|
|
7c50a916c7 | ||
|
|
48b4eca28a | ||
|
|
e94bcf03cb | ||
|
|
8ca02794c5 | ||
|
|
ef5184cf1c | ||
|
|
9e27624a19 | ||
|
|
cb971ad654 | ||
|
|
cf202f3718 | ||
|
|
18fcc03b3c | ||
|
|
6fa3faec65 | ||
|
|
75e6ec85e1 | ||
|
|
a253d1557d | ||
|
|
9afadca5dc | ||
|
|
fb57cd26a1 | ||
|
|
b193d40d22 | ||
|
|
6c045c9beb | ||
|
|
040a01a5dd | ||
|
|
8cc5ad8742 | ||
|
|
540c1696d1 | ||
|
|
08091ff3e3 | ||
|
|
b51c1544b9 | ||
|
|
7dfe35eca6 | ||
|
|
69768ec71e | ||
|
|
e4d55e4ee4 | ||
|
|
c4bc7ce7e2 | ||
|
|
211da31b34 | ||
|
|
b832942b6e | ||
|
|
33ea0fdfe8 | ||
|
|
18123ff958 | ||
|
|
ceb829cc33 | ||
|
|
b34d3d5882 | ||
|
|
26a1d4fc37 | ||
|
|
d03afd6f4b |
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: New Issue (Developers Only)
|
||||
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue"
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
https://yuzu-emu.org/wiki/faq/
|
||||
|
||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
If the FAQ does not answer your question, please go to:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
When submitting an issue, please check the following:
|
||||
|
||||
- You have read the above.
|
||||
- You have provided the version (commit hash) of yuzu you are using.
|
||||
- You have provided sufficient detail for the issue to be reproduced.
|
||||
- You have provided system specs (if relevant).
|
||||
- Please also provide:
|
||||
- For any issues, a log file
|
||||
- For crashes, a backtrace.
|
||||
- For graphical issues, comparison screenshots with real hardware.
|
||||
- For emulation inaccuracies, a test-case (if able).
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Affected Build(s)
|
||||
description: List the affected build(s) that this issue applies to.
|
||||
placeholder: Mainline 1234 / Early Access 1234
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue-desc
|
||||
attributes:
|
||||
label: Description of Issue
|
||||
description: A brief description of the issue encountered along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Log File
|
||||
description: A log file will help our developers to better diagnose and fix the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-config
|
||||
attributes:
|
||||
label: System Configuration
|
||||
placeholder: |
|
||||
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||
RAM: 16GB DDR4-3200
|
||||
OS: Windows 11 22H2 (Build 22621.819)
|
||||
value: |
|
||||
CPU:
|
||||
GPU/Driver:
|
||||
RAM:
|
||||
OS:
|
||||
validations:
|
||||
required: true
|
||||
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Feature Request
|
||||
description: File a feature request
|
||||
labels: "request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-feature
|
||||
attributes:
|
||||
label: What feature are you suggesting?
|
||||
description: A brief description of the requested feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: why-feature
|
||||
attributes:
|
||||
label: Why would this feature be useful?
|
||||
description: A brief description of why this feature would make yuzu better.
|
||||
validations:
|
||||
required: true
|
||||
39
.github/workflows/python-publish.yml
vendored
Normal file
39
.github/workflows/python-publish.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install build
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
@@ -132,10 +132,6 @@ Files: vcpkg.json
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-3.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/config.yml
|
||||
Copyright: 2020 tgsm <doodrabbit@hotmail.com>
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md
|
||||
Copyright: 2016 MerryMage
|
||||
Files: .github/ISSUE_TEMPLATE/*
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
@@ -92,10 +92,14 @@ endif()
|
||||
add_subdirectory(sirit)
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
else()
|
||||
if (NOT WIN32)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32 OR NOT OPENSSL_FOUND)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 424fdb5c50...07c614f91b
4
externals/microprofile/microprofileui.h
vendored
4
externals/microprofile/microprofileui.h
vendored
@@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
MicroProfile& S = *MicroProfileGet();
|
||||
MP_DEBUG_DUMP_RANGE();
|
||||
int nY = nBaseY - UI.nOffsetY;
|
||||
int64_t nNumBoxes = 0;
|
||||
int64_t nNumLines = 0;
|
||||
|
||||
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
|
||||
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
|
||||
@@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++nNumBoxes;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
nLinesDrawn[nStackPos] = nLineX;
|
||||
MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
|
||||
++nNumLines;
|
||||
}
|
||||
}
|
||||
nStackPos--;
|
||||
|
||||
@@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe
|
||||
|
||||
cmd.session_id = session_id;
|
||||
|
||||
cmd.input_count = parameter.input_count;
|
||||
s16 max_input{0};
|
||||
for (u32 i = 0; i < parameter.input_count; i++) {
|
||||
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
|
||||
max_input = std::max(max_input, cmd.inputs[i]);
|
||||
}
|
||||
|
||||
if (state.upsampler_info != nullptr) {
|
||||
const auto size_{state.upsampler_info->sample_count * parameter.input_count};
|
||||
const auto size_bytes{size_ * sizeof(s32)};
|
||||
const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
|
||||
cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
|
||||
parameter.input_count * state.upsampler_info->sample_count};
|
||||
(max_input + 1) * state.upsampler_info->sample_count};
|
||||
} else {
|
||||
cmd.sample_buffer = samples_buffer;
|
||||
}
|
||||
|
||||
cmd.input_count = parameter.input_count;
|
||||
for (u32 i = 0; i < parameter.input_count; i++) {
|
||||
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
|
||||
}
|
||||
|
||||
GenerateEnd<DeviceSinkCommand>(cmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
|
||||
impl = std::make_unique<
|
||||
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ struct Values {
|
||||
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
|
||||
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
|
||||
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
||||
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
|
||||
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
|
||||
ShaderBackend::SPIRV, "shader_backend"};
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
||||
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
||||
|
||||
@@ -4,14 +4,27 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/x64/cpu_detect.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
return _xgetbv(index);
|
||||
}
|
||||
#else
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
|
||||
}
|
||||
|
||||
#define _XCR_XFEATURE_ENABLED_MASK 0
|
||||
static inline u64 _xgetbv(u32 index) {
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
u32 eax, edx;
|
||||
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
||||
return ((u64)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
namespace Common {
|
||||
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
caps.avx = true;
|
||||
if (Common::Bit<12>(cpu_id[2]))
|
||||
caps.fma = true;
|
||||
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
|
||||
return caps;
|
||||
}
|
||||
|
||||
std::optional<int> GetProcessorCount() {
|
||||
#if defined(_WIN32)
|
||||
// Get the buffer length.
|
||||
DWORD length = 0;
|
||||
GetLogicalProcessorInformation(nullptr, &length);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
|
||||
length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
|
||||
// Now query the core count.
|
||||
if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int>(
|
||||
std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
|
||||
return proc_info.Relationship == RelationProcessorCore;
|
||||
}));
|
||||
#elif defined(__unix__)
|
||||
const int thread_count = std::thread::hardware_concurrency();
|
||||
std::ifstream smt("/sys/devices/system/cpu/smt/active");
|
||||
char state = '0';
|
||||
if (smt) {
|
||||
smt.read(&state, sizeof(state));
|
||||
}
|
||||
switch (state) {
|
||||
case '0':
|
||||
return thread_count;
|
||||
case '1':
|
||||
return thread_count / 2;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
// Shame on you
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -74,4 +75,7 @@ struct CPUCaps {
|
||||
*/
|
||||
const CPUCaps& GetCPUCaps();
|
||||
|
||||
/// Detects CPU core count
|
||||
std::optional<int> GetProcessorCount();
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -120,6 +120,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/cabinet.cpp
|
||||
frontend/applets/cabinet.h
|
||||
frontend/applets/controller.cpp
|
||||
frontend/applets/controller.h
|
||||
frontend/applets/error.cpp
|
||||
@@ -312,6 +314,8 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applet_cabinet.cpp
|
||||
hle/service/am/applets/applet_cabinet.h
|
||||
hle/service/am/applets/applet_controller.cpp
|
||||
hle/service/am/applets/applet_controller.h
|
||||
hle/service/am/applets/applet_error.cpp
|
||||
@@ -526,6 +530,11 @@ add_library(core STATIC
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
hle/service/nfc/nfc.h
|
||||
hle/service/nfc/nfc_device.cpp
|
||||
hle/service/nfc/nfc_device.h
|
||||
hle/service/nfc/nfc_result.h
|
||||
hle/service/nfc/nfc_user.cpp
|
||||
hle/service/nfc/nfc_user.h
|
||||
hle/service/nfp/amiibo_crypto.cpp
|
||||
hle/service/nfp/amiibo_crypto.h
|
||||
hle/service/nfp/nfp.cpp
|
||||
|
||||
@@ -348,7 +348,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
|
||||
acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
|
||||
if (!error.failed()) {
|
||||
c(peer_socket);
|
||||
AsyncAccept(acceptor, c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
@@ -59,9 +68,7 @@ namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
@@ -70,39 +77,42 @@ public:
|
||||
}
|
||||
|
||||
bool SignalDebugger(SignalInfo signal_info) {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
info = signal_info;
|
||||
if (stopped || !state) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
state->info = signal_info;
|
||||
|
||||
// Write a single byte into the pipe to wake up the debug interface.
|
||||
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// These functions are callbacks from the frontend, and the lock will be held.
|
||||
// There is no need to relock it.
|
||||
|
||||
std::span<const u8> ReadFromClient() override {
|
||||
return ReceiveInto(client_socket, client_data);
|
||||
return ReceiveInto(state->client_socket, state->client_data);
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
boost::asio::write(state->client_socket,
|
||||
boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
active_thread = thread;
|
||||
state->active_thread = thread;
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
return active_thread;
|
||||
return state->active_thread;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -113,65 +123,78 @@ private:
|
||||
|
||||
// Run the connection thread.
|
||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
try {
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
|
||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
||||
io_context.run_one();
|
||||
io_context.restart();
|
||||
AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
|
||||
ThreadLoop(stop_token);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
|
||||
LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
|
||||
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
// Ensure everything is stopped.
|
||||
PauseEmulation();
|
||||
|
||||
// Set up the new frontend.
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
|
||||
// Set the new state. This will tear down any existing state.
|
||||
state = ConnectionState{
|
||||
.client_socket{std::move(peer)},
|
||||
.signal_pipe{io_context},
|
||||
.info{},
|
||||
.active_thread{},
|
||||
.client_data{},
|
||||
.pipe_data{},
|
||||
};
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
}
|
||||
|
||||
void ShutdownServer() {
|
||||
connection_thread.request_stop();
|
||||
io_context.stop();
|
||||
connection_thread.join();
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
|
||||
// Main event loop.
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
switch (info.type) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
switch (state->info.type) {
|
||||
case SignalType::Stopped:
|
||||
case SignalType::Watchpoint:
|
||||
// Stop emulation.
|
||||
PauseEmulation();
|
||||
|
||||
// Notify the client.
|
||||
active_thread = info.thread;
|
||||
state->active_thread = state->info.thread;
|
||||
UpdateActiveThread();
|
||||
|
||||
if (info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(active_thread, *info.watchpoint);
|
||||
if (state->info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
|
||||
} else {
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -179,8 +202,8 @@ private:
|
||||
frontend->ShuttingDown();
|
||||
|
||||
// Wait for emulation to shut down gracefully now.
|
||||
signal_pipe.close();
|
||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
state->signal_pipe.close();
|
||||
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||
|
||||
break;
|
||||
@@ -188,17 +211,16 @@ private:
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
const auto actions{frontend->ClientData(data)};
|
||||
for (const auto action : actions) {
|
||||
switch (action) {
|
||||
case DebuggerAction::Interrupt: {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = true;
|
||||
}
|
||||
stopped = true;
|
||||
PauseEmulation();
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::Continue:
|
||||
@@ -206,15 +228,15 @@ private:
|
||||
break;
|
||||
case DebuggerAction::StepThreadUnlocked:
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(active_thread);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(state->active_thread);
|
||||
});
|
||||
break;
|
||||
case DebuggerAction::StepThreadLocked: {
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -254,15 +276,14 @@ private:
|
||||
template <typename Callback>
|
||||
void MarkResumed(Callback&& cb) {
|
||||
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
||||
std::scoped_lock cl{connection_lock};
|
||||
stopped = false;
|
||||
cb();
|
||||
}
|
||||
|
||||
void UpdateActiveThread() {
|
||||
const auto& threads{ThreadList()};
|
||||
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
|
||||
active_thread = threads[0];
|
||||
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
|
||||
state->active_thread = threads[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,18 +295,22 @@ private:
|
||||
System& system;
|
||||
std::unique_ptr<DebuggerFrontend> frontend;
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
std::jthread connection_thread;
|
||||
std::mutex connection_lock;
|
||||
boost::asio::io_context io_context;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
bool pipe_data;
|
||||
bool stopped;
|
||||
struct ConnectionState {
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
std::array<u8, 4096> client_data;
|
||||
bool pipe_data;
|
||||
};
|
||||
|
||||
std::optional<ConnectionState> state{};
|
||||
bool stopped{};
|
||||
};
|
||||
|
||||
Debugger::Debugger(Core::System& system, u16 port) {
|
||||
|
||||
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
} else if (command.starts_with("StartNoAckMode")) {
|
||||
no_ack = true;
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else if (command.starts_with("Rcmd,")) {
|
||||
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
||||
}
|
||||
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
|
||||
{"----- Free -----", Kernel::Svc::MemoryState::Free},
|
||||
{"Io ", Kernel::Svc::MemoryState::Io},
|
||||
{"Static ", Kernel::Svc::MemoryState::Static},
|
||||
{"Code ", Kernel::Svc::MemoryState::Code},
|
||||
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
|
||||
{"Normal ", Kernel::Svc::MemoryState::Normal},
|
||||
{"Shared ", Kernel::Svc::MemoryState::Shared},
|
||||
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
|
||||
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
|
||||
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
|
||||
{"Stack ", Kernel::Svc::MemoryState::Stack},
|
||||
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
|
||||
{"Transfered ", Kernel::Svc::MemoryState::Transfered},
|
||||
{"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
|
||||
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
|
||||
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
|
||||
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
|
||||
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
|
||||
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
|
||||
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
|
||||
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
|
||||
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
|
||||
}};
|
||||
|
||||
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
|
||||
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
|
||||
if (std::get<1>(MemoryStateNames[i]) == state) {
|
||||
return std::get<0>(MemoryStateNames[i]);
|
||||
}
|
||||
}
|
||||
return "Unknown ";
|
||||
}
|
||||
|
||||
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
|
||||
if (info.state == Kernel::Svc::MemoryState::Free) {
|
||||
return " ";
|
||||
}
|
||||
|
||||
switch (info.permission) {
|
||||
case Kernel::Svc::MemoryPermission::ReadExecute:
|
||||
return "r-x";
|
||||
case Kernel::Svc::MemoryPermission::Read:
|
||||
return "r--";
|
||||
case Kernel::Svc::MemoryPermission::ReadWrite:
|
||||
return "rw-";
|
||||
default:
|
||||
return "---";
|
||||
}
|
||||
}
|
||||
|
||||
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
|
||||
Kernel::Svc::MemoryInfo mem_info;
|
||||
VAddr cur_addr{base};
|
||||
|
||||
// Expect: r-x Code (.text)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: r-- Code (.rodata)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: rw- CodeData (.data)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
||||
std::string reply;
|
||||
|
||||
auto* process = system.CurrentProcess();
|
||||
auto& page_table = process->PageTable();
|
||||
|
||||
if (command_str == "get info") {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
reply = fmt::format("Process: {:#x} ({})\n"
|
||||
"Program Id: {:#018x}\n",
|
||||
process->GetProcessID(), process->GetName(), process->GetProgramID());
|
||||
reply +=
|
||||
fmt::format("Layout:\n"
|
||||
" Alias: {:#012x} - {:#012x}\n"
|
||||
" Heap: {:#012x} - {:#012x}\n"
|
||||
" Aslr: {:#012x} - {:#012x}\n"
|
||||
" Stack: {:#012x} - {:#012x}\n"
|
||||
"Modules:\n",
|
||||
page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
|
||||
page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
|
||||
page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
|
||||
page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
|
||||
|
||||
for (const auto& [vaddr, name] : modules) {
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||
GetModuleEnd(page_table, vaddr), name);
|
||||
}
|
||||
} else if (command_str == "get mappings") {
|
||||
reply = "Mappings:\n";
|
||||
VAddr cur_addr = 0;
|
||||
|
||||
while (true) {
|
||||
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
|
||||
|
||||
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
|
||||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
|
||||
const char* state = GetMemoryStateName(mem_info.state);
|
||||
const char* perm = GetMemoryPermissionString(mem_info);
|
||||
|
||||
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||
|
||||
reply +=
|
||||
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
||||
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
||||
}
|
||||
|
||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||
if (next_address <= cur_addr) {
|
||||
break;
|
||||
}
|
||||
|
||||
cur_addr = next_address;
|
||||
}
|
||||
} else if (command_str == "help") {
|
||||
reply = "Commands:\n get info\n get mappings\n";
|
||||
} else {
|
||||
reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
|
||||
}
|
||||
|
||||
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
|
||||
SendReply(Common::HexToString(reply_span, false));
|
||||
}
|
||||
|
||||
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
||||
for (auto* thread : threads) {
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
||||
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
|
||||
void HandleQuery(std::string_view command);
|
||||
void HandleRcmd(const std::vector<u8>& command);
|
||||
void HandleBreakpointInsert(std::string_view command);
|
||||
void HandleBreakpointRemove(std::string_view command);
|
||||
std::vector<char>::const_iterator CommandEnd() const;
|
||||
|
||||
20
src/core/frontend/applets/cabinet.cpp
Normal file
20
src/core/frontend/applets/cabinet.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
CabinetApplet::~CabinetApplet() = default;
|
||||
|
||||
void DefaultCabinetApplet::ShowCabinetApplet(
|
||||
const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
callback(false, {});
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
37
src/core/frontend/applets/cabinet.h
Normal file
37
src/core/frontend/applets/cabinet.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
} // namespace Service::NFP
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
struct CabinetParameters {
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
Service::NFP::CabinetMode mode;
|
||||
};
|
||||
|
||||
using CabinetCallback = std::function<void(bool, const std::string&)>;
|
||||
|
||||
class CabinetApplet {
|
||||
public:
|
||||
virtual ~CabinetApplet();
|
||||
virtual void ShowCabinetApplet(const CabinetCallback& callback,
|
||||
const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
|
||||
};
|
||||
|
||||
class DefaultCabinetApplet final : public CabinetApplet {
|
||||
public:
|
||||
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouchParams() {
|
||||
// TODO(german77): Support any number of fingers
|
||||
std::size_t index = 0;
|
||||
|
||||
// Hardcode mouse, touchscreen and cemuhook parameters
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
if (!Settings::values.mouse_enabled) {
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
|
||||
Common::ParamPackage touchscreen_param{};
|
||||
touchscreen_param.Set("engine", "touch");
|
||||
touchscreen_param.Set("axis_x", i * 2);
|
||||
touchscreen_param.Set("axis_y", (i * 2) + 1);
|
||||
touchscreen_param.Set("button", i);
|
||||
touch_params[index++] = touchscreen_param;
|
||||
}
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
|
||||
|
||||
// Map the rest of the fingers from touch from button configuration
|
||||
for (const auto& config_entry : touch_buttons) {
|
||||
if (index >= touch_params.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
continue;
|
||||
}
|
||||
Common::ParamPackage params{config_entry};
|
||||
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
|
||||
touch_button_params.Set("button", params.Serialize());
|
||||
touch_button_params.Set("x", x);
|
||||
touch_button_params.Set("y", y);
|
||||
touch_button_params.Set("touch_id", static_cast<int>(index));
|
||||
touch_params[index] = touch_button_params;
|
||||
index++;
|
||||
}
|
||||
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= console.touch_values.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
console.touch_values[index] = TransformToTouch(callback);
|
||||
const auto touch_input = TransformToTouch(callback);
|
||||
auto touch_index = GetIndexFromFingerId(index);
|
||||
bool is_new_input = false;
|
||||
|
||||
if (!touch_index.has_value() && touch_input.pressed.value) {
|
||||
touch_index = GetNextFreeIndex();
|
||||
is_new_input = true;
|
||||
}
|
||||
|
||||
// No free entries or invalid state. Ignore input
|
||||
if (!touch_index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& touch_value = console.touch_values[touch_index.value()];
|
||||
|
||||
if (is_new_input) {
|
||||
touch_value.pressed.value = true;
|
||||
touch_value.id = static_cast<u32>(index);
|
||||
}
|
||||
|
||||
touch_value.x = touch_input.x;
|
||||
touch_value.y = touch_input.y;
|
||||
|
||||
if (!touch_input.pressed.value) {
|
||||
touch_value.pressed.value = false;
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(german77): Remap touch id in sequential order
|
||||
console.touch_state[index] = {
|
||||
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
|
||||
.id = static_cast<u32>(console.touch_values[index].id),
|
||||
.pressed = console.touch_values[index].pressed.value,
|
||||
// Touch outside allowed range. Ignore input
|
||||
if (touch_index.value() >= MaxActiveTouchInputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.touch_state[touch_index.value()] = {
|
||||
.position = {touch_value.x.value, touch_value.y.value},
|
||||
.id = static_cast<u32>(touch_index.value()),
|
||||
.pressed = touch_input.pressed.value,
|
||||
};
|
||||
|
||||
lock.unlock();
|
||||
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
|
||||
return console.touch_state;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
const auto& finger = console.touch_values[index];
|
||||
if (!finger.pressed.value) {
|
||||
continue;
|
||||
}
|
||||
if (finger.id == static_cast<int>(finger_id)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
if (!console.touch_values[index].pressed.value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
@@ -20,6 +21,8 @@
|
||||
#include "core/hid/motion_input.h"
|
||||
|
||||
namespace Core::HID {
|
||||
static constexpr std::size_t MaxTouchDevices = 32;
|
||||
static constexpr std::size_t MaxActiveTouchInputs = 16;
|
||||
|
||||
struct ConsoleMotionInfo {
|
||||
Common::Input::MotionStatus raw_status{};
|
||||
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
|
||||
};
|
||||
|
||||
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionParams = Common::ParamPackage;
|
||||
using TouchParams = std::array<Common::ParamPackage, 16>;
|
||||
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, 16>;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
||||
|
||||
struct TouchFinger {
|
||||
u64 last_touch{};
|
||||
@@ -55,7 +58,7 @@ struct ConsoleMotion {
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
using TouchFingerState = std::array<TouchFinger, 16>;
|
||||
using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
|
||||
|
||||
struct ConsoleStatus {
|
||||
// Data from input_common
|
||||
@@ -166,6 +169,10 @@ private:
|
||||
*/
|
||||
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the console status
|
||||
* @param type Input type of the event to trigger
|
||||
|
||||
@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
u32* CommandBuffer() {
|
||||
[[nodiscard]] u32* CommandBuffer() {
|
||||
return cmd_buf.data();
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
* Returns the session through which this request was made. This can be used as a map key to
|
||||
* access per-client data on services.
|
||||
*/
|
||||
Kernel::KServerSession* Session() {
|
||||
[[nodiscard]] Kernel::KServerSession* Session() {
|
||||
return server_session;
|
||||
}
|
||||
|
||||
@@ -217,61 +217,61 @@ public:
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
|
||||
|
||||
u32_le GetHipcCommand() const {
|
||||
[[nodiscard]] u32_le GetHipcCommand() const {
|
||||
return command;
|
||||
}
|
||||
|
||||
u32_le GetTipcCommand() const {
|
||||
[[nodiscard]] u32_le GetTipcCommand() const {
|
||||
return static_cast<u32_le>(command_header->type.Value()) -
|
||||
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
|
||||
}
|
||||
|
||||
u32_le GetCommand() const {
|
||||
[[nodiscard]] u32_le GetCommand() const {
|
||||
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
|
||||
}
|
||||
|
||||
bool IsTipc() const {
|
||||
[[nodiscard]] bool IsTipc() const {
|
||||
return command_header->IsTipc();
|
||||
}
|
||||
|
||||
IPC::CommandType GetCommandType() const {
|
||||
[[nodiscard]] IPC::CommandType GetCommandType() const {
|
||||
return command_header->type;
|
||||
}
|
||||
|
||||
u64 GetPID() const {
|
||||
[[nodiscard]] u64 GetPID() const {
|
||||
return pid;
|
||||
}
|
||||
|
||||
u32 GetDataPayloadOffset() const {
|
||||
[[nodiscard]] u32 GetDataPayloadOffset() const {
|
||||
return data_payload_offset;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
return buffer_x_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
return buffer_a_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
return buffer_b_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
|
||||
[[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
|
||||
return domain_message_header.value();
|
||||
}
|
||||
|
||||
bool HasDomainMessageHeader() const {
|
||||
[[nodiscard]] bool HasDomainMessageHeader() const {
|
||||
return domain_message_header.has_value();
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
std::size_t WriteBuffer(const void* buffer, std::size_t size,
|
||||
@@ -308,22 +308,34 @@ public:
|
||||
}
|
||||
|
||||
/// Helper function to get the size of the input buffer
|
||||
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get the size of the output buffer
|
||||
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to derive the number of elements able to be contained in the read buffer
|
||||
template <typename T>
|
||||
[[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const {
|
||||
return GetReadBufferSize(buffer_index) / sizeof(T);
|
||||
}
|
||||
|
||||
/// Helper function to derive the number of elements able to be contained in the write buffer
|
||||
template <typename T>
|
||||
[[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const {
|
||||
return GetWriteBufferSize(buffer_index) / sizeof(T);
|
||||
}
|
||||
|
||||
/// Helper function to test whether the input buffer at buffer_index can be read
|
||||
bool CanReadBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to test whether the output buffer at buffer_index can be written
|
||||
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
Handle GetCopyHandle(std::size_t index) const {
|
||||
[[nodiscard]] Handle GetCopyHandle(std::size_t index) const {
|
||||
return incoming_copy_handles.at(index);
|
||||
}
|
||||
|
||||
Handle GetMoveHandle(std::size_t index) const {
|
||||
[[nodiscard]] Handle GetMoveHandle(std::size_t index) const {
|
||||
return incoming_move_handles.at(index);
|
||||
}
|
||||
|
||||
@@ -348,13 +360,13 @@ public:
|
||||
manager = manager_;
|
||||
}
|
||||
|
||||
std::string Description() const;
|
||||
[[nodiscard]] std::string Description() const;
|
||||
|
||||
KThread& GetThread() {
|
||||
[[nodiscard]] KThread& GetThread() {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
return manager.lock();
|
||||
}
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
// If we somehow get an invalid type, abort.
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// If we've hit the end of a gap, free it.
|
||||
|
||||
@@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) {
|
||||
m_readable_event.Initialize(this);
|
||||
|
||||
// Set our owner process.
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
// HACK: this should never be nullptr, but service threads don't have a
|
||||
// proper parent process yet.
|
||||
if (owner != nullptr) {
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
}
|
||||
|
||||
// Mark initialized.
|
||||
m_initialized = true;
|
||||
@@ -50,8 +54,11 @@ Result KEvent::Clear() {
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
|
||||
owner->Close();
|
||||
|
||||
if (owner != nullptr) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
|
||||
owner->Close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -82,6 +83,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
|
||||
KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = cur_thread->GetOwnerProcess();
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return cur_thread;
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
|
||||
Result KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
@@ -113,21 +113,7 @@ public:
|
||||
return this->GetObjectImpl(handle);
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process =
|
||||
static_cast<KAutoObject*>(static_cast<void*>(cur_thread->GetOwnerProcess()));
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return static_cast<KAutoObject*>(cur_thread);
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
|
||||
@@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
addr += size;
|
||||
@@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -320,6 +320,9 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionStart() const {
|
||||
return m_alias_code_region_start;
|
||||
}
|
||||
constexpr VAddr GetAliasCodeRegionEnd() const {
|
||||
return m_alias_code_region_end;
|
||||
}
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return m_alias_code_region_end - m_alias_code_region_start;
|
||||
}
|
||||
|
||||
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create TLS region
|
||||
|
||||
@@ -40,7 +40,6 @@ private:
|
||||
std::mutex m_session_mutex;
|
||||
std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
|
||||
KEvent* m_wakeup_event;
|
||||
KProcess* m_process;
|
||||
KThread* m_thread;
|
||||
std::atomic<bool> m_shutdown_requested;
|
||||
const std::string m_service_name;
|
||||
@@ -180,39 +179,17 @@ ServiceThread::Impl::~Impl() {
|
||||
|
||||
// Close thread.
|
||||
m_thread->Close();
|
||||
|
||||
// Close process.
|
||||
m_process->Close();
|
||||
}
|
||||
|
||||
ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
|
||||
: kernel{kernel_}, m_service_name{service_name} {
|
||||
// Initialize process.
|
||||
m_process = KProcess::Create(kernel);
|
||||
KProcess::Initialize(m_process, kernel.System(), service_name,
|
||||
KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
|
||||
|
||||
// Reserve a new event from the process resource limit
|
||||
KScopedResourceReservation event_reservation(m_process, LimitableResource::EventCountMax);
|
||||
ASSERT(event_reservation.Succeeded());
|
||||
|
||||
// Initialize event.
|
||||
m_wakeup_event = KEvent::Create(kernel);
|
||||
m_wakeup_event->Initialize(m_process);
|
||||
|
||||
// Commit the event reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
// Reserve a new thread from the process resource limit
|
||||
KScopedResourceReservation thread_reservation(m_process, LimitableResource::ThreadCountMax);
|
||||
ASSERT(thread_reservation.Succeeded());
|
||||
m_wakeup_event->Initialize(nullptr);
|
||||
|
||||
// Initialize thread.
|
||||
m_thread = KThread::Create(kernel);
|
||||
ASSERT(KThread::InitializeDummyThread(m_thread, m_process).IsSuccess());
|
||||
|
||||
// Commit the thread reservation.
|
||||
thread_reservation.Commit();
|
||||
ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
|
||||
|
||||
// Start thread.
|
||||
m_host_thread = std::jthread([this] { LoopProcess(); });
|
||||
|
||||
@@ -28,30 +28,49 @@ enum class ErrorModule : u32 {
|
||||
Loader = 9,
|
||||
CMIF = 10,
|
||||
HIPC = 11,
|
||||
TMA = 12,
|
||||
DMNT = 13,
|
||||
GDS = 14,
|
||||
PM = 15,
|
||||
NS = 16,
|
||||
BSDSockets = 17,
|
||||
HTC = 18,
|
||||
TSC = 19,
|
||||
NCMContent = 20,
|
||||
SM = 21,
|
||||
RO = 22,
|
||||
GC = 23,
|
||||
SDMMC = 24,
|
||||
OVLN = 25,
|
||||
SPL = 26,
|
||||
Socket = 27,
|
||||
HTCLOW = 29,
|
||||
DDSF = 30,
|
||||
HTCFS = 31,
|
||||
Async = 32,
|
||||
Util = 33,
|
||||
TIPC = 35,
|
||||
ANIF = 37,
|
||||
ETHC = 100,
|
||||
I2C = 101,
|
||||
GPIO = 102,
|
||||
UART = 103,
|
||||
CPAD = 104,
|
||||
Settings = 105,
|
||||
FTM = 106,
|
||||
WLAN = 107,
|
||||
XCD = 108,
|
||||
TMP451 = 109,
|
||||
NIFM = 110,
|
||||
Hwopus = 111,
|
||||
LSM6DS3 = 112,
|
||||
Bluetooth = 113,
|
||||
VI = 114,
|
||||
NFP = 115,
|
||||
Time = 116,
|
||||
FGM = 117,
|
||||
OE = 118,
|
||||
BH1730FVC = 119,
|
||||
PCIe = 120,
|
||||
Friends = 121,
|
||||
BCAT = 122,
|
||||
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
|
||||
AHID = 130,
|
||||
Qlaunch = 132,
|
||||
PCV = 133,
|
||||
OMM = 134,
|
||||
USBPD = 134,
|
||||
BPC = 135,
|
||||
PSM = 136,
|
||||
NIM = 137,
|
||||
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
|
||||
NSD = 141,
|
||||
PCTL = 142,
|
||||
BTM = 143,
|
||||
LA = 144,
|
||||
ETicket = 145,
|
||||
NGC = 146,
|
||||
ERPT = 147,
|
||||
APM = 148,
|
||||
CEC = 149,
|
||||
Profiler = 150,
|
||||
ErrorUpload = 151,
|
||||
LIDBE = 152,
|
||||
Audio = 153,
|
||||
NPNS = 154,
|
||||
NPNSHTTPSTREAM = 155,
|
||||
ARP = 157,
|
||||
SWKBD = 158,
|
||||
BOOT = 159,
|
||||
NetDiag = 160,
|
||||
NFCMifare = 161,
|
||||
UserlandAssert = 162,
|
||||
Fatal = 163,
|
||||
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
|
||||
SPSM = 165,
|
||||
BGTC = 167,
|
||||
UserlandCrash = 168,
|
||||
SASBUS = 169,
|
||||
PI = 170,
|
||||
AudioCtrl = 172,
|
||||
LBL = 173,
|
||||
JIT = 175,
|
||||
HDCP = 176,
|
||||
OMM = 177,
|
||||
PDM = 178,
|
||||
OLSC = 179,
|
||||
SREPO = 180,
|
||||
Dauth = 181,
|
||||
STDFU = 182,
|
||||
DBG = 183,
|
||||
DHCPS = 186,
|
||||
SPI = 187,
|
||||
AVM = 188,
|
||||
PWM = 189,
|
||||
RTC = 191,
|
||||
Regulator = 192,
|
||||
LED = 193,
|
||||
SIO = 195,
|
||||
PCM = 196,
|
||||
CLKRST = 197,
|
||||
POWCTL = 198,
|
||||
AudioOld = 201,
|
||||
HID = 202,
|
||||
LDN = 203,
|
||||
CS = 204,
|
||||
Irsensor = 205,
|
||||
Capture = 206,
|
||||
Manu = 208,
|
||||
ATK = 209,
|
||||
WEB = 210,
|
||||
LCS = 211,
|
||||
GRC = 212,
|
||||
Repair = 213,
|
||||
Album = 214,
|
||||
RID = 215,
|
||||
Migration = 216,
|
||||
MigrationLdcServ = 217,
|
||||
HIDBUS = 218,
|
||||
ENS = 219,
|
||||
WebSocket = 223,
|
||||
DCDMTP = 227,
|
||||
PGL = 228,
|
||||
Notification = 229,
|
||||
INS = 230,
|
||||
LP2P = 231,
|
||||
RCD = 232,
|
||||
LCM40607 = 233,
|
||||
PRC = 235,
|
||||
TMAHTC = 237,
|
||||
ECTX = 238,
|
||||
MNPP = 239,
|
||||
HSHL = 240,
|
||||
CAPMTP = 242,
|
||||
DP2HDMI = 244,
|
||||
Cradle = 245,
|
||||
SProfile = 246,
|
||||
NDRM = 250,
|
||||
TSPM = 499,
|
||||
DevMenu = 500,
|
||||
GeneralWebApplet = 800,
|
||||
WifiWebAuthApplet = 809,
|
||||
WhitelistedApplet = 810,
|
||||
|
||||
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_)
|
||||
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
|
||||
system_,
|
||||
"CabinetApplet"} {
|
||||
|
||||
availability_change_event =
|
||||
service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
Cabinet::~Cabinet() = default;
|
||||
|
||||
void Cabinet::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
|
||||
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
|
||||
common_args.arguments_version, common_args.library_version,
|
||||
common_args.play_startup_sound, common_args.size, common_args.system_tick,
|
||||
common_args.theme_color);
|
||||
|
||||
const auto storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
|
||||
const auto applet_input_data = storage->GetData();
|
||||
ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
|
||||
|
||||
std::memcpy(&applet_input_common, applet_input_data.data(),
|
||||
sizeof(StartParamForAmiiboSettings));
|
||||
}
|
||||
|
||||
bool Cabinet::TransactionComplete() const {
|
||||
return is_complete;
|
||||
}
|
||||
|
||||
Result Cabinet::GetStatus() const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Cabinet::ExecuteInteractive() {
|
||||
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
|
||||
}
|
||||
|
||||
void Cabinet::Execute() {
|
||||
if (is_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
|
||||
DisplayCompleted(apply_changes, amiibo_name);
|
||||
};
|
||||
|
||||
// TODO: listen on all controllers
|
||||
if (nfp_device == nullptr) {
|
||||
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
|
||||
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
|
||||
nfp_device->Initialize();
|
||||
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
|
||||
}
|
||||
|
||||
const Core::Frontend::CabinetParameters parameters{
|
||||
.tag_info = applet_input_common.tag_info,
|
||||
.register_info = applet_input_common.register_info,
|
||||
.mode = applet_input_common.applet_mode,
|
||||
};
|
||||
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
frontend.ShowCabinetApplet(callback, parameters, nfp_device);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
DisplayCompleted(false, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
|
||||
Service::Mii::MiiManager manager;
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
|
||||
if (!apply_changes) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
|
||||
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
|
||||
nfp_device->Mount(Service::NFP::MountTarget::All);
|
||||
}
|
||||
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
|
||||
Service::NFP::AmiiboName name{};
|
||||
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
|
||||
nfp_device->SetNicknameAndOwner(name);
|
||||
break;
|
||||
}
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
nfp_device->DeleteApplicationArea();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
nfp_device->RestoreAmiibo();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
nfp_device->DeleteAllData();
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
applet_output.device_handle = applet_input_common.device_handle;
|
||||
applet_output.result = CabinetResult::Cancel;
|
||||
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
|
||||
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
|
||||
nfp_device->Finalize();
|
||||
|
||||
if (reg_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::RegisterInfo;
|
||||
}
|
||||
|
||||
if (tag_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::TagInfo;
|
||||
}
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
void Cabinet::Cancel() {
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
applet_output.device_handle = applet_input_common.device_handle;
|
||||
applet_output.result = CabinetResult::Cancel;
|
||||
nfp_device->Finalize();
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class CabinetAppletVersion : u32 {
|
||||
Version1 = 0x1,
|
||||
};
|
||||
|
||||
enum class CabinetResult : u8 {
|
||||
Cancel = 0,
|
||||
TagInfo = 1 << 1,
|
||||
RegisterInfo = 1 << 2,
|
||||
All = TagInfo | RegisterInfo,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
|
||||
|
||||
// This is nn::nfp::AmiiboSettingsStartParam
|
||||
struct AmiiboSettingsStartParam {
|
||||
u64 device_handle;
|
||||
std::array<u8, 0x20> param_1;
|
||||
u8 param_2;
|
||||
};
|
||||
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
|
||||
"AmiiboSettingsStartParam is an invalid size");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// This is nn::nfp::StartParamForAmiiboSettings
|
||||
struct StartParamForAmiiboSettings {
|
||||
u8 param_1;
|
||||
Service::NFP::CabinetMode applet_mode;
|
||||
u8 flags;
|
||||
u8 amiibo_settings_1;
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
std::array<u8, 0x20> amiibo_settings_3;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
|
||||
"StartParamForAmiiboSettings is an invalid size");
|
||||
|
||||
// This is nn::nfp::ReturnValueForAmiiboSettings
|
||||
struct ReturnValueForAmiiboSettings {
|
||||
CabinetResult result;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
|
||||
"ReturnValueForAmiiboSettings is an invalid size");
|
||||
#pragma pack(pop)
|
||||
|
||||
class Cabinet final : public Applet {
|
||||
public:
|
||||
explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_);
|
||||
~Cabinet() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
const Core::Frontend::CabinetApplet& frontend;
|
||||
Core::System& system;
|
||||
|
||||
bool is_complete{false};
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
|
||||
Kernel::KEvent* availability_change_event;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
StartParamForAmiiboSettings applet_input_common{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -144,6 +144,7 @@ void Error::Initialize() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ void Auth::Execute() {
|
||||
}
|
||||
default:
|
||||
unimplemented_log();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ void PhotoViewer::Execute() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
#include "core/hle/service/am/applets/applet_controller.h"
|
||||
#include "core/hle/service/am/applets/applet_error.h"
|
||||
#include "core/hle/service/am/applets/applet_general_backend.h"
|
||||
@@ -171,13 +173,15 @@ void Applet::Initialize() {
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
|
||||
ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
MiiEdit mii_edit_,
|
||||
ParentalControlsApplet parental_controls_applet,
|
||||
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
|
||||
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
|
||||
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
|
||||
mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
|
||||
: cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
|
||||
error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
|
||||
parental_controls{std::move(parental_controls_applet)},
|
||||
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
|
||||
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
|
||||
|
||||
@@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
|
||||
}
|
||||
|
||||
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
|
||||
if (set.cabinet != nullptr) {
|
||||
frontend.cabinet = std::move(set.cabinet);
|
||||
}
|
||||
|
||||
if (set.controller != nullptr) {
|
||||
frontend.controller = std::move(set.controller);
|
||||
}
|
||||
@@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() {
|
||||
}
|
||||
|
||||
void AppletManager::SetDefaultAppletsIfMissing() {
|
||||
if (frontend.cabinet == nullptr) {
|
||||
frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
|
||||
}
|
||||
|
||||
if (frontend.controller == nullptr) {
|
||||
frontend.controller =
|
||||
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
|
||||
@@ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
|
||||
switch (id) {
|
||||
case AppletId::Auth:
|
||||
return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
|
||||
case AppletId::Cabinet:
|
||||
return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
|
||||
case AppletId::Controller:
|
||||
return std::make_shared<Controller>(system, mode, *frontend.controller);
|
||||
case AppletId::Error:
|
||||
|
||||
@@ -16,6 +16,7 @@ class System;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class CabinetApplet;
|
||||
class ControllerApplet;
|
||||
class ECommerceApplet;
|
||||
class ErrorApplet;
|
||||
@@ -176,6 +177,7 @@ protected:
|
||||
};
|
||||
|
||||
struct AppletFrontendSet {
|
||||
using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
|
||||
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
|
||||
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
|
||||
using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
|
||||
@@ -186,10 +188,11 @@ struct AppletFrontendSet {
|
||||
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
|
||||
|
||||
AppletFrontendSet();
|
||||
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
|
||||
MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
|
||||
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
|
||||
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
|
||||
AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
|
||||
ErrorApplet error_applet, MiiEdit mii_edit_,
|
||||
ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
|
||||
ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
|
||||
WebBrowser web_browser_);
|
||||
~AppletFrontendSet();
|
||||
|
||||
AppletFrontendSet(const AppletFrontendSet&) = delete;
|
||||
@@ -198,6 +201,7 @@ struct AppletFrontendSet {
|
||||
AppletFrontendSet(AppletFrontendSet&&) noexcept;
|
||||
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
|
||||
|
||||
CabinetApplet cabinet;
|
||||
ControllerApplet controller;
|
||||
ErrorApplet error;
|
||||
MiiEdit mii_edit;
|
||||
|
||||
@@ -122,10 +122,10 @@ private:
|
||||
}
|
||||
|
||||
void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
|
||||
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
|
||||
std::vector<u64> released_buffers(write_buffer_size, 0);
|
||||
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
|
||||
std::vector<u64> released_buffers(write_buffer_size);
|
||||
|
||||
auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
const auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
|
||||
[[maybe_unused]] std::string tags{};
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
@@ -228,7 +228,7 @@ void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
u32 out_count{0};
|
||||
@@ -248,7 +248,7 @@ void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
|
||||
u32 out_count{0};
|
||||
|
||||
@@ -129,16 +129,16 @@ private:
|
||||
}
|
||||
|
||||
void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
|
||||
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
|
||||
std::vector<u64> released_buffers(write_buffer_size, 0);
|
||||
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
|
||||
std::vector<u64> released_buffers(write_buffer_size);
|
||||
|
||||
auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
const auto count = impl->GetReleasedBuffers(released_buffers);
|
||||
|
||||
[[maybe_unused]] std::string tags{};
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
tags += fmt::format("{:08X}, ", released_buffers[i]);
|
||||
}
|
||||
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
[[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()};
|
||||
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
|
||||
tags);
|
||||
|
||||
@@ -244,7 +244,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
|
||||
std::scoped_lock l{impl->mutex};
|
||||
|
||||
const auto write_count =
|
||||
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
|
||||
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
|
||||
std::vector<AudioDevice::AudioDeviceName> device_names{};
|
||||
if (write_count > 0) {
|
||||
device_names.emplace_back("DeviceOut");
|
||||
|
||||
@@ -274,7 +274,7 @@ public:
|
||||
|
||||
private:
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
|
||||
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
|
||||
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
@@ -335,7 +335,7 @@ private:
|
||||
}
|
||||
|
||||
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
|
||||
const auto write_size = ctx.GetWriteBufferSize();
|
||||
std::string out_name{"AudioTvOutput"};
|
||||
|
||||
LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
|
||||
@@ -387,7 +387,7 @@ private:
|
||||
}
|
||||
|
||||
void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
|
||||
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
|
||||
|
||||
std::vector<AudioDevice::AudioDeviceName> out_names{};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
ExtraBehavior extra_behavior) {
|
||||
u32 consumed = 0;
|
||||
u32 sample_count = 0;
|
||||
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
|
||||
std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>());
|
||||
|
||||
if (extra_behavior == ExtraBehavior::ResetContext) {
|
||||
ResetDecoderContext();
|
||||
|
||||
@@ -443,7 +443,7 @@ private:
|
||||
}
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
|
||||
auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
|
||||
|
||||
LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
|
||||
|
||||
@@ -533,7 +533,7 @@ private:
|
||||
}
|
||||
|
||||
void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
|
||||
auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
|
||||
auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
|
||||
|
||||
LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
|
||||
|
||||
|
||||
@@ -192,12 +192,10 @@ private:
|
||||
}
|
||||
|
||||
void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) {
|
||||
u32 out_entries;
|
||||
if (keys.GetCommonTickets().empty())
|
||||
out_entries = 0;
|
||||
else
|
||||
out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
|
||||
|
||||
size_t out_entries = 0;
|
||||
if (!keys.GetCommonTickets().empty()) {
|
||||
out_entries = ctx.GetWriteBufferNumElements<u128>();
|
||||
}
|
||||
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
|
||||
|
||||
keys.PopulateTickets();
|
||||
@@ -206,20 +204,19 @@ private:
|
||||
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
|
||||
[](const auto& pair) { return pair.first; });
|
||||
|
||||
out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
|
||||
out_entries = std::min(ids.size(), out_entries);
|
||||
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(out_entries);
|
||||
rb.Push<u32>(static_cast<u32>(out_entries));
|
||||
}
|
||||
|
||||
void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) {
|
||||
u32 out_entries;
|
||||
if (keys.GetPersonalizedTickets().empty())
|
||||
out_entries = 0;
|
||||
else
|
||||
out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
|
||||
size_t out_entries = 0;
|
||||
if (!keys.GetPersonalizedTickets().empty()) {
|
||||
out_entries = ctx.GetWriteBufferNumElements<u128>();
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
|
||||
|
||||
@@ -229,12 +226,12 @@ private:
|
||||
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
|
||||
[](const auto& pair) { return pair.first; });
|
||||
|
||||
out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
|
||||
out_entries = std::min(ids.size(), out_entries);
|
||||
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(out_entries);
|
||||
rb.Push<u32>(static_cast<u32>(out_entries));
|
||||
}
|
||||
|
||||
void GetCommonTicketSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -277,7 +277,7 @@ private:
|
||||
LOG_DEBUG(Service_FS, "called.");
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>();
|
||||
|
||||
// Cap at total number of entries.
|
||||
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
|
||||
@@ -543,7 +543,7 @@ public:
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
|
||||
const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>();
|
||||
|
||||
// Cap at total number of entries.
|
||||
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
|
||||
|
||||
@@ -36,8 +36,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
||||
// Correct pad_update_ns is 4ms this is overclocked to lower input lag
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
||||
// Correct npad_update_ns is 4ms this is overclocked to lower input lag
|
||||
constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
|
||||
constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
|
||||
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||
|
||||
@@ -75,8 +76,16 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
|
||||
|
||||
// Register update callbacks
|
||||
pad_update_event = Core::Timing::CreateEvent(
|
||||
npad_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateNpad(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
default_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateDefaultCallback",
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
@@ -100,7 +109,9 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
|
||||
default_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
|
||||
mouse_keyboard_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
|
||||
@@ -118,7 +129,8 @@ void IAppletResource::DeactivateController(HidController controller) {
|
||||
}
|
||||
|
||||
IAppletResource::~IAppletResource() {
|
||||
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
||||
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
||||
}
|
||||
@@ -144,10 +156,20 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
|
||||
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
|
||||
continue;
|
||||
}
|
||||
// Npad has it's own update event
|
||||
if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
|
||||
continue;
|
||||
}
|
||||
controller->OnUpdate(core_timing);
|
||||
}
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||
std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
@@ -71,12 +71,14 @@ private:
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> pad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> npad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> default_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
|
||||
void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
|
||||
const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
|
||||
const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1);
|
||||
|
||||
if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
|
||||
LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
|
||||
@@ -333,7 +333,7 @@ public:
|
||||
const auto channel{rp.PopEnum<WifiChannel>()};
|
||||
const auto scan_filter{rp.PopRaw<ScanFilter>()};
|
||||
|
||||
const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
|
||||
const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>();
|
||||
|
||||
if (network_info_size == 0) {
|
||||
LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfc/nfc_user.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
@@ -97,76 +98,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_) : ServiceFramework{system_, "NFC::IUser"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::InitializeOld, "InitializeOld"},
|
||||
{1, &IUser::FinalizeOld, "FinalizeOld"},
|
||||
{2, &IUser::GetStateOld, "GetStateOld"},
|
||||
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
|
||||
{400, &IUser::InitializeOld, "Initialize"},
|
||||
{401, &IUser::FinalizeOld, "Finalize"},
|
||||
{402, &IUser::GetStateOld, "GetState"},
|
||||
{403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
|
||||
{404, nullptr, "ListDevices"},
|
||||
{405, nullptr, "GetDeviceState"},
|
||||
{406, nullptr, "GetNpadId"},
|
||||
{407, nullptr, "AttachAvailabilityChangeEvent"},
|
||||
{408, nullptr, "StartDetection"},
|
||||
{409, nullptr, "StopDetection"},
|
||||
{410, nullptr, "GetTagInfo"},
|
||||
{411, nullptr, "AttachActivateEvent"},
|
||||
{412, nullptr, "AttachDeactivateEvent"},
|
||||
{1000, nullptr, "ReadMifare"},
|
||||
{1001, nullptr, "WriteMifare"},
|
||||
{1300, nullptr, "SendCommandByPassThrough"},
|
||||
{1301, nullptr, "KeepPassThroughSession"},
|
||||
{1302, nullptr, "ReleasePassThroughSession"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class NfcStates : u32 {
|
||||
Finalized = 6,
|
||||
};
|
||||
|
||||
void InitializeOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||
rb.Push(ResultSuccess);
|
||||
// We don't deal with hardware initialization so we can just stub this.
|
||||
}
|
||||
|
||||
void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u8>(true);
|
||||
}
|
||||
|
||||
void GetStateOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
|
||||
}
|
||||
|
||||
void FinalizeOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
class NFC_U final : public ServiceFramework<NFC_U> {
|
||||
public:
|
||||
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
|
||||
|
||||
197
src/core/hle/service/nfc/nfc_device.cpp
Normal file
197
src/core/hle/service/nfc/nfc_device.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/input.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/nfc/nfc_device.h"
|
||||
#include "core/hle/service/nfc/nfc_result.h"
|
||||
#include "core/hle/service/nfc/nfc_user.h"
|
||||
|
||||
namespace Service::NFC {
|
||||
NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_,
|
||||
Kernel::KEvent* availability_change_event_)
|
||||
: npad_id{npad_id_}, system{system_}, service_context{service_context_},
|
||||
availability_change_event{availability_change_event_} {
|
||||
activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
|
||||
deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
|
||||
npad_device = system.HIDCore().GetEmulatedController(npad_id);
|
||||
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
|
||||
.is_npad_service = false,
|
||||
};
|
||||
is_controller_set = true;
|
||||
callback_key = npad_device->SetCallback(engine_callback);
|
||||
}
|
||||
|
||||
NfcDevice::~NfcDevice() {
|
||||
activate_event->Close();
|
||||
deactivate_event->Close();
|
||||
if (!is_controller_set) {
|
||||
return;
|
||||
}
|
||||
npad_device->DeleteCallback(callback_key);
|
||||
is_controller_set = false;
|
||||
};
|
||||
|
||||
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Connected ||
|
||||
type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != Core::HID::ControllerTriggerType::Nfc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!npad_device->IsConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nfc_status = npad_device->GetNfc();
|
||||
switch (nfc_status.state) {
|
||||
case Common::Input::NfcState::NewAmiibo:
|
||||
LoadNfcTag(nfc_status.data);
|
||||
break;
|
||||
case Common::Input::NfcState::AmiiboRemoved:
|
||||
if (device_state != NFP::DeviceState::SearchingForTag) {
|
||||
CloseNfcTag();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
|
||||
if (device_state != NFP::DeviceState::SearchingForTag) {
|
||||
LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.size() != sizeof(NFP::EncryptedNTAG215File)) {
|
||||
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
|
||||
|
||||
device_state = NFP::DeviceState::TagFound;
|
||||
deactivate_event->GetReadableEvent().Clear();
|
||||
activate_event->Signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NfcDevice::CloseNfcTag() {
|
||||
LOG_INFO(Service_NFC, "Remove nfc tag");
|
||||
|
||||
device_state = NFP::DeviceState::TagRemoved;
|
||||
encrypted_tag_data = {};
|
||||
activate_event->GetReadableEvent().Clear();
|
||||
deactivate_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
|
||||
return activate_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
|
||||
return deactivate_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
void NfcDevice::Initialize() {
|
||||
device_state =
|
||||
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
|
||||
encrypted_tag_data = {};
|
||||
}
|
||||
|
||||
void NfcDevice::Finalize() {
|
||||
if (device_state == NFP::DeviceState::SearchingForTag ||
|
||||
device_state == NFP::DeviceState::TagRemoved) {
|
||||
StopDetection();
|
||||
}
|
||||
device_state = NFP::DeviceState::Unavailable;
|
||||
}
|
||||
|
||||
Result NfcDevice::StartDetection(s32 protocol_) {
|
||||
if (device_state != NFP::DeviceState::Initialized &&
|
||||
device_state != NFP::DeviceState::TagRemoved) {
|
||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
|
||||
LOG_ERROR(Service_NFC, "Nfc not supported");
|
||||
return NfcDisabled;
|
||||
}
|
||||
|
||||
device_state = NFP::DeviceState::SearchingForTag;
|
||||
protocol = protocol_;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfcDevice::StopDetection() {
|
||||
npad_device->SetPollingMode(Common::Input::PollingMode::Active);
|
||||
|
||||
if (device_state == NFP::DeviceState::Initialized) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
if (device_state == NFP::DeviceState::TagFound ||
|
||||
device_state == NFP::DeviceState::TagMounted) {
|
||||
CloseNfcTag();
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (device_state == NFP::DeviceState::SearchingForTag ||
|
||||
device_state == NFP::DeviceState::TagRemoved) {
|
||||
device_state = NFP::DeviceState::Initialized;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
|
||||
if (device_state != NFP::DeviceState::TagFound &&
|
||||
device_state != NFP::DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
|
||||
if (device_state == NFP::DeviceState::TagRemoved) {
|
||||
return TagRemoved;
|
||||
}
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
// Protocol and tag type may change here
|
||||
tag_info = {
|
||||
.uuid = encrypted_tag_data.uuid.uid,
|
||||
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
|
||||
.protocol = NFP::TagProtocol::TypeA,
|
||||
.tag_type = NFP::TagType::Type2,
|
||||
};
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u64 NfcDevice::GetHandle() const {
|
||||
// Generate a handle based of the npad id
|
||||
return static_cast<u64>(npad_id);
|
||||
}
|
||||
|
||||
NFP::DeviceState NfcDevice::GetCurrentState() const {
|
||||
return device_state;
|
||||
}
|
||||
|
||||
Core::HID::NpadIdType NfcDevice::GetNpadId() const {
|
||||
return npad_id;
|
||||
}
|
||||
|
||||
} // namespace Service::NFC
|
||||
70
src/core/hle/service/nfc/nfc_device.h
Normal file
70
src/core/hle/service/nfc/nfc_device.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Core::HID {
|
||||
class EmulatedController;
|
||||
enum class ControllerTriggerType;
|
||||
enum class NpadIdType : u32;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::NFC {
|
||||
class NfcDevice {
|
||||
public:
|
||||
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_,
|
||||
Kernel::KEvent* availability_change_event_);
|
||||
~NfcDevice();
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
Result StartDetection(s32 protocol_);
|
||||
Result StopDetection();
|
||||
|
||||
Result GetTagInfo(NFP::TagInfo& tag_info) const;
|
||||
|
||||
u64 GetHandle() const;
|
||||
NFP::DeviceState GetCurrentState() const;
|
||||
Core::HID::NpadIdType GetNpadId() const;
|
||||
|
||||
Kernel::KReadableEvent& GetActivateEvent() const;
|
||||
Kernel::KReadableEvent& GetDeactivateEvent() const;
|
||||
|
||||
private:
|
||||
void NpadUpdate(Core::HID::ControllerTriggerType type);
|
||||
bool LoadNfcTag(std::span<const u8> data);
|
||||
void CloseNfcTag();
|
||||
|
||||
bool is_controller_set{};
|
||||
int callback_key;
|
||||
const Core::HID::NpadIdType npad_id;
|
||||
Core::System& system;
|
||||
Core::HID::EmulatedController* npad_device = nullptr;
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
Kernel::KEvent* activate_event = nullptr;
|
||||
Kernel::KEvent* deactivate_event = nullptr;
|
||||
Kernel::KEvent* availability_change_event = nullptr;
|
||||
|
||||
s32 protocol{};
|
||||
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
|
||||
|
||||
NFP::EncryptedNTAG215File encrypted_tag_data{};
|
||||
};
|
||||
|
||||
} // namespace Service::NFC
|
||||
17
src/core/hle/service/nfc/nfc_result.h
Normal file
17
src/core/hle/service/nfc/nfc_result.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::NFC {
|
||||
|
||||
constexpr Result DeviceNotFound(ErrorModule::NFC, 64);
|
||||
constexpr Result InvalidArgument(ErrorModule::NFC, 65);
|
||||
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
|
||||
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
|
||||
constexpr Result TagRemoved(ErrorModule::NFC, 97);
|
||||
constexpr Result CorruptedData(ErrorModule::NFC, 144);
|
||||
|
||||
} // namespace Service::NFC
|
||||
365
src/core/hle/service/nfc/nfc_user.cpp
Normal file
365
src/core/hle/service/nfc/nfc_user.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/nfc/nfc_device.h"
|
||||
#include "core/hle/service/nfc/nfc_result.h"
|
||||
#include "core/hle/service/nfc/nfc_user.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Service::NFC {
|
||||
|
||||
IUser::IUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "InitializeOld"},
|
||||
{1, &IUser::Finalize, "FinalizeOld"},
|
||||
{2, &IUser::GetState, "GetStateOld"},
|
||||
{3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
|
||||
{400, &IUser::Initialize, "Initialize"},
|
||||
{401, &IUser::Finalize, "Finalize"},
|
||||
{402, &IUser::GetState, "GetState"},
|
||||
{403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
|
||||
{404, &IUser::ListDevices, "ListDevices"},
|
||||
{405, &IUser::GetDeviceState, "GetDeviceState"},
|
||||
{406, &IUser::GetNpadId, "GetNpadId"},
|
||||
{407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{408, &IUser::StartDetection, "StartDetection"},
|
||||
{409, &IUser::StopDetection, "StopDetection"},
|
||||
{410, &IUser::GetTagInfo, "GetTagInfo"},
|
||||
{411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{1000, nullptr, "ReadMifare"},
|
||||
{1001, nullptr, "WriteMifare"},
|
||||
{1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
|
||||
{1301, nullptr, "KeepPassThroughSession"},
|
||||
{1302, nullptr, "ReleasePassThroughSession"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
|
||||
|
||||
for (u32 device_index = 0; device_index < 10; device_index++) {
|
||||
devices[device_index] =
|
||||
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
|
||||
service_context, availability_change_event);
|
||||
}
|
||||
}
|
||||
|
||||
IUser ::~IUser() {
|
||||
availability_change_event->Close();
|
||||
}
|
||||
|
||||
void IUser::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Initialize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IUser::Finalize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
|
||||
state = State::NonInitialized;
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->Finalize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IUser::GetState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(state);
|
||||
}
|
||||
|
||||
void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(state != State::NonInitialized);
|
||||
}
|
||||
|
||||
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.CanWriteBuffer()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(InvalidArgument);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.GetWriteBufferSize() == 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(InvalidArgument);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u64> nfp_devices;
|
||||
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
|
||||
|
||||
for (auto& device : devices) {
|
||||
if (nfp_devices.size() >= max_allowed_devices) {
|
||||
continue;
|
||||
}
|
||||
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
|
||||
nfp_devices.push_back(device->GetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
if (nfp_devices.empty()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(nfp_devices);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<s32>(nfp_devices.size()));
|
||||
}
|
||||
|
||||
void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(device.value()->GetCurrentState());
|
||||
}
|
||||
|
||||
void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(device.value()->GetNpadId());
|
||||
}
|
||||
|
||||
void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_protocol{rp.Pop<s32>()};
|
||||
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->StartDetection(nfp_protocol);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = device.value()->StopDetection();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
NFP::TagInfo tag_info{};
|
||||
const auto result = device.value()->GetTagInfo(tag_info);
|
||||
ctx.WriteBuffer(tag_info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(device.value()->GetActivateEvent());
|
||||
}
|
||||
|
||||
void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
|
||||
}
|
||||
|
||||
void IUser::SendCommandByPassThrough(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
|
||||
const auto command_data{ctx.ReadBuffer()};
|
||||
|
||||
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
|
||||
device_handle, timeout.ToSeconds(), command_data.size());
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(NfcDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = GetNfcDevice(device_handle);
|
||||
|
||||
if (!device.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> out_data(1);
|
||||
// TODO: Request data from nfc device
|
||||
ctx.WriteBuffer(out_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(out_data.size()));
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
|
||||
for (auto& device : devices) {
|
||||
if (device->GetHandle() == handle) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Service::NFC
|
||||
52
src/core/hle/service/nfc/nfc_user.h
Normal file
52
src/core/hle/service/nfc/nfc_user.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NFC {
|
||||
class NfcDevice;
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_);
|
||||
~IUser();
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void Finalize(Kernel::HLERequestContext& ctx);
|
||||
void GetState(Kernel::HLERequestContext& ctx);
|
||||
void IsNfcEnabled(Kernel::HLERequestContext& ctx);
|
||||
void ListDevices(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceState(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadId(Kernel::HLERequestContext& ctx);
|
||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
|
||||
void StartDetection(Kernel::HLERequestContext& ctx);
|
||||
void StopDetection(Kernel::HLERequestContext& ctx);
|
||||
void GetTagInfo(Kernel::HLERequestContext& ctx);
|
||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void SendCommandByPassThrough(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
|
||||
|
||||
State state{State::NonInitialized};
|
||||
Kernel::KEvent* availability_change_event;
|
||||
};
|
||||
|
||||
} // namespace Service::NFC
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/nfp/amiibo_crypto.h"
|
||||
|
||||
namespace Service::NFP::AmiiboCrypto {
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/input.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -19,7 +16,6 @@
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
#include "core/hle/service/nfp/amiibo_crypto.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
#include "core/hle/service/nfp/nfp_result.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
@@ -49,6 +45,8 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
|
||||
}
|
||||
|
||||
NfpDevice::~NfpDevice() {
|
||||
activate_event->Close();
|
||||
deactivate_event->Close();
|
||||
if (!is_controller_set) {
|
||||
return;
|
||||
}
|
||||
@@ -77,6 +75,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
LoadAmiibo(nfc_status.data);
|
||||
break;
|
||||
case Common::Input::NfcState::AmiiboRemoved:
|
||||
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
|
||||
break;
|
||||
}
|
||||
if (device_state != DeviceState::SearchingForTag) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
@@ -97,6 +98,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Filter by allowed_protocols here
|
||||
|
||||
memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
|
||||
|
||||
device_state = DeviceState::TagFound;
|
||||
@@ -143,7 +146,7 @@ void NfpDevice::Finalize() {
|
||||
device_state = DeviceState::Unavailable;
|
||||
}
|
||||
|
||||
Result NfpDevice::StartDetection(s32 protocol_) {
|
||||
Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
|
||||
if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
return WrongDeviceState;
|
||||
@@ -155,7 +158,7 @@ Result NfpDevice::StartDetection(s32 protocol_) {
|
||||
}
|
||||
|
||||
device_state = DeviceState::SearchingForTag;
|
||||
protocol = protocol_;
|
||||
allowed_protocols = allowed_protocol;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -469,6 +472,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
|
||||
application_area_id = {};
|
||||
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
if (device_state == DeviceState::TagRemoved) {
|
||||
return TagRemoved;
|
||||
}
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
|
||||
LOG_WARNING(Service_NFP, "Application area is not initialized");
|
||||
return ApplicationAreaIsNotInitialized;
|
||||
}
|
||||
|
||||
application_area_id = tag_data.application_area_id;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
Result StartDetection(s32 protocol_);
|
||||
Result StartDetection(TagProtocol allowed_protocol);
|
||||
Result StopDetection();
|
||||
Result Mount(MountTarget mount_target);
|
||||
Result Unmount();
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
Result DeleteAllData();
|
||||
|
||||
Result OpenApplicationArea(u32 access_id);
|
||||
Result GetApplicationAreaId(u32& application_area_id) const;
|
||||
Result GetApplicationArea(std::vector<u8>& data) const;
|
||||
Result SetApplicationArea(std::span<const u8> data);
|
||||
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||
@@ -88,7 +89,7 @@ private:
|
||||
|
||||
bool is_data_moddified{};
|
||||
bool is_app_area_open{};
|
||||
s32 protocol{};
|
||||
TagProtocol allowed_protocols{};
|
||||
s64 current_posix_time{};
|
||||
MountTarget mount_target{MountTarget::None};
|
||||
DeviceState device_state{DeviceState::Unavailable};
|
||||
|
||||
@@ -88,11 +88,22 @@ enum class PackedTagType : u8 {
|
||||
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
|
||||
};
|
||||
|
||||
// Verify this enum. It might be completely wrong default protocol is 0x48
|
||||
enum class TagProtocol : u32 {
|
||||
None,
|
||||
TypeA, // ISO14443A
|
||||
TypeB, // ISO14443B
|
||||
TypeF, // Sony Felica
|
||||
TypeA = 1U << 0, // ISO14443A
|
||||
TypeB = 1U << 1, // ISO14443B
|
||||
TypeF = 1U << 2, // Sony Felica
|
||||
Unknown1 = 1U << 3,
|
||||
Unknown2 = 1U << 5,
|
||||
All = 0xFFFFFFFFU,
|
||||
};
|
||||
|
||||
enum class CabinetMode : u8 {
|
||||
StartNicknameAndOwnerSettings,
|
||||
StartGameDataEraser,
|
||||
StartRestorer,
|
||||
StartFormatter,
|
||||
};
|
||||
|
||||
using UniqueSerialNumber = std::array<u8, 7>;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
@@ -55,8 +52,12 @@ IUser::IUser(Core::System& system_)
|
||||
}
|
||||
}
|
||||
|
||||
IUser ::~IUser() {
|
||||
availability_change_event->Close();
|
||||
}
|
||||
|
||||
void IUser::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_NFC, "called");
|
||||
LOG_INFO(Service_NFP, "called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
@@ -64,7 +65,7 @@ void IUser::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
device->Initialize();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -103,9 +104,9 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
std::vector<u64> nfp_devices;
|
||||
const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64);
|
||||
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
|
||||
|
||||
for (auto& device : devices) {
|
||||
for (const auto& device : devices) {
|
||||
if (nfp_devices.size() >= max_allowed_devices) {
|
||||
continue;
|
||||
}
|
||||
@@ -114,7 +115,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if (nfp_devices.size() == 0) {
|
||||
if (nfp_devices.empty()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(DeviceNotFound);
|
||||
return;
|
||||
@@ -130,7 +131,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto nfp_protocol{rp.Pop<s32>()};
|
||||
const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
|
||||
|
||||
if (state == State::NonInitialized) {
|
||||
@@ -551,9 +552,9 @@ void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IUser::GetState(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NFC, "called");
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3, 0};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(state);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -12,6 +16,7 @@ class NfpDevice;
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Core::System& system_);
|
||||
~IUser();
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
|
||||
@@ -279,13 +279,10 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestC
|
||||
font_sizes.push_back(region.size);
|
||||
}
|
||||
|
||||
// Resize buffers if game requests smaller size output.
|
||||
font_codes.resize(
|
||||
std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
|
||||
font_offsets.resize(
|
||||
std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
|
||||
font_sizes.resize(
|
||||
std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
|
||||
// Resize buffers if game requests smaller size output
|
||||
font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements<u32>(0)));
|
||||
font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements<u32>(1)));
|
||||
font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements<u32>(2)));
|
||||
|
||||
ctx.WriteBuffer(font_codes, 0);
|
||||
ctx.WriteBuffer(font_offsets, 1);
|
||||
|
||||
@@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
|
||||
return error_notifier_event;
|
||||
case 2:
|
||||
return unknown_event;
|
||||
default: {
|
||||
default:
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
|
||||
return sm_exception_breakpoint_pause_report_event;
|
||||
case 3:
|
||||
return error_notifier_event;
|
||||
default: {
|
||||
default:
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() {
|
||||
}
|
||||
|
||||
void BufferQueueCore::SignalDequeueCondition() {
|
||||
dequeue_possible.store(true);
|
||||
dequeue_condition.notify_all();
|
||||
}
|
||||
|
||||
bool BufferQueueCore::WaitForDequeueCondition() {
|
||||
bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
|
||||
if (is_shutting_down) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dequeue_condition.wait(mutex);
|
||||
dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
|
||||
dequeue_possible.store(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
private:
|
||||
void SignalDequeueCondition();
|
||||
bool WaitForDequeueCondition();
|
||||
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
|
||||
|
||||
s32 GetMinUndequeuedBufferCountLocked(bool async) const;
|
||||
s32 GetMinMaxBufferCountLocked(bool async) const;
|
||||
@@ -60,7 +60,8 @@ private:
|
||||
BufferQueueDefs::SlotsType slots{};
|
||||
std::vector<BufferItem> queue;
|
||||
s32 override_max_buffer_count{};
|
||||
mutable std::condition_variable_any dequeue_condition;
|
||||
std::condition_variable dequeue_condition;
|
||||
std::atomic<bool> dequeue_possible{};
|
||||
const bool use_async_buffer{}; // This is always disabled on HOS
|
||||
bool dequeue_buffer_cannot_block{};
|
||||
PixelFormat default_buffer_format{PixelFormat::Rgba8888};
|
||||
|
||||
@@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
|
||||
Status* return_flags) const {
|
||||
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
|
||||
std::unique_lock<std::mutex>& lk) const {
|
||||
bool try_again = true;
|
||||
|
||||
while (try_again) {
|
||||
@@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
|
||||
return Status::WouldBlock;
|
||||
}
|
||||
|
||||
if (!core->WaitForDequeueCondition()) {
|
||||
if (!core->WaitForDequeueCondition(lk)) {
|
||||
// We are no longer running
|
||||
return Status::NoError;
|
||||
}
|
||||
@@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||
Status return_flags = Status::NoError;
|
||||
bool attached_by_consumer = false;
|
||||
{
|
||||
std::scoped_lock lock{core->mutex};
|
||||
std::unique_lock lock{core->mutex};
|
||||
core->WaitWhileAllocatingLocked();
|
||||
|
||||
if (format == PixelFormat::NoFormat) {
|
||||
@@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||
usage |= core->consumer_usage_bit;
|
||||
|
||||
s32 found{};
|
||||
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
|
||||
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);
|
||||
if (status != Status::NoError) {
|
||||
return status;
|
||||
}
|
||||
@@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
std::unique_lock lock{core->mutex};
|
||||
core->WaitWhileAllocatingLocked();
|
||||
|
||||
Status return_flags = Status::NoError;
|
||||
s32 found{};
|
||||
|
||||
const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
|
||||
const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);
|
||||
if (status != Status::NoError) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ public:
|
||||
private:
|
||||
BufferQueueProducer(const BufferQueueProducer&) = delete;
|
||||
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
|
||||
std::unique_lock<std::mutex>& lk) const;
|
||||
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
Service::KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
@@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
|
||||
break;
|
||||
}
|
||||
|
||||
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||
|
||||
@@ -83,7 +83,7 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
|
||||
}
|
||||
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
|
||||
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
|
||||
const std::size_t requested_amount = ctx.GetWriteBufferNumElements<LanguageCode>();
|
||||
const std::size_t max_amount = std::min(requested_amount, max_entries);
|
||||
const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
|
||||
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
|
||||
|
||||
@@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return value + rule.transition_time + offset;
|
||||
}
|
||||
|
||||
@@ -761,7 +761,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
u64 src_address =
|
||||
GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
|
||||
u64 src_value = 0;
|
||||
switch (store_static->bit_width) {
|
||||
switch (begin_cond->bit_width) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
|
||||
@@ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
amiibo_data = data;
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
@@ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
return LoadAmiibo(file_path);
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||
: State::Initialized;
|
||||
@@ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
std::string VirtualAmiibo::GetLastFilePath() const {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -47,8 +47,11 @@ public:
|
||||
State GetCurrentState() const;
|
||||
|
||||
Info LoadAmiibo(const std::string& amiibo_file);
|
||||
Info ReloadAmiibo();
|
||||
Info CloseAmiibo();
|
||||
|
||||
std::string GetLastFilePath() const;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t amiibo_size = 0x21C;
|
||||
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace InputCommon {
|
||||
class TouchFromButtonDevice final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
TouchFromButtonDevice(Button button_, float x_, float y_)
|
||||
: button(std::move(button_)), x(x_), y(y_) {
|
||||
last_button_value = false;
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
@@ -34,7 +34,6 @@ public:
|
||||
.pressed = button_status,
|
||||
.x = {},
|
||||
.y = {},
|
||||
.id = touch_id,
|
||||
};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
@@ -62,7 +61,6 @@ public:
|
||||
private:
|
||||
Button button;
|
||||
bool last_button_value;
|
||||
const int touch_id;
|
||||
const float x;
|
||||
const float y;
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
@@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("button", null_engine));
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const float x = params.Get("x", 0.0f) / 1280.0f;
|
||||
const float y = params.Get("y", 0.0f) / 720.0f;
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
return Common::Input::CameraError::NotSupported;
|
||||
}
|
||||
|
||||
// Request nfc data from a controller
|
||||
// Returns success if nfc is supported
|
||||
virtual Common::Input::NfcState SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier) const {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
|
||||
@@ -229,13 +229,12 @@ private:
|
||||
|
||||
class InputFromTouch final : public Common::Input::InputDevice {
|
||||
public:
|
||||
explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_,
|
||||
bool inverted_, int axis_x_, int axis_y_,
|
||||
Common::Input::AnalogProperties properties_x_,
|
||||
explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
|
||||
int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
|
||||
Common::Input::AnalogProperties properties_y_,
|
||||
InputEngine* input_engine_)
|
||||
: identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_),
|
||||
inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
|
||||
: identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
|
||||
axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
|
||||
properties_y(properties_y_), input_engine(input_engine_) {
|
||||
UpdateCallback engine_callback{[this]() { OnChange(); }};
|
||||
const InputIdentifier button_input_identifier{
|
||||
@@ -271,8 +270,7 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::TouchStatus GetStatus() const {
|
||||
Common::Input::TouchStatus status;
|
||||
status.id = touch_id;
|
||||
Common::Input::TouchStatus status{};
|
||||
status.pressed = {
|
||||
.value = input_engine->GetButton(identifier, button),
|
||||
.inverted = inverted,
|
||||
@@ -307,7 +305,6 @@ public:
|
||||
|
||||
private:
|
||||
const PadIdentifier identifier;
|
||||
const int touch_id;
|
||||
const int button;
|
||||
const bool toggle;
|
||||
const bool inverted;
|
||||
@@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
|
||||
const Common::ParamPackage& params) {
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
|
||||
const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
|
||||
const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
|
||||
@@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
|
||||
input_engine->PreSetAxis(identifier, axis_x);
|
||||
input_engine->PreSetAxis(identifier, axis_y);
|
||||
input_engine->PreSetButton(identifier, button);
|
||||
return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x,
|
||||
axis_y, properties_x, properties_y, input_engine.get());
|
||||
return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
|
||||
properties_x, properties_y, input_engine.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
|
||||
|
||||
@@ -320,6 +320,7 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
header += "OPTION ARB_draw_buffers;";
|
||||
header += "OPTION ARB_fragment_layer_viewport;";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::Layer:
|
||||
ctx.Add("MOV.F {}.x,fragment.layer;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -205,6 +205,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::Layer:
|
||||
ctx.AddF32("{}=itof(gl_Layer);", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -315,6 +315,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
case IR::Attribute::Layer:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.layer));
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
|
||||
@@ -1359,6 +1359,11 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (loads[IR::Attribute::PrimitiveId]) {
|
||||
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
||||
}
|
||||
if (loads[IR::Attribute::Layer]) {
|
||||
AddCapability(spv::Capability::Geometry);
|
||||
layer = DefineInput(*this, U32[1], false, spv::BuiltIn::Layer);
|
||||
Decorate(layer, spv::Decoration::Flat);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const bool is_fragment{stage != Stage::Fragment};
|
||||
const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
|
||||
|
||||
@@ -28,6 +28,10 @@ add_library(video_core STATIC
|
||||
dirty_flags.h
|
||||
dma_pusher.cpp
|
||||
dma_pusher.h
|
||||
engines/sw_blitter/blitter.cpp
|
||||
engines/sw_blitter/blitter.h
|
||||
engines/sw_blitter/converter.cpp
|
||||
engines/sw_blitter/converter.h
|
||||
engines/const_buffer_info.h
|
||||
engines/engine_interface.h
|
||||
engines/engine_upload.cpp
|
||||
|
||||
@@ -992,7 +992,20 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
TouchBuffer(buffer, index_buffer.buffer_id);
|
||||
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
|
||||
const u32 size = index_buffer.size;
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
if (maxwell3d->inline_index_draw_indexes.size()) {
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
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(),
|
||||
maxwell3d->inline_index_draw_indexes.data(), size);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
|
||||
} else {
|
||||
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
|
||||
}
|
||||
} else {
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
}
|
||||
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
|
||||
maxwell3d->regs.index_buffer.FormatSizeInBytes();
|
||||
@@ -1275,7 +1288,15 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
}
|
||||
flags[Dirty::IndexBuffer] = false;
|
||||
last_index_count = index_array.count;
|
||||
|
||||
if (maxwell3d->inline_index_draw_indexes.size()) {
|
||||
auto inline_index_size = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
|
||||
index_buffer = Binding{
|
||||
.cpu_addr = 0,
|
||||
.size = inline_index_size,
|
||||
.buffer_id = CreateBuffer(0, inline_index_size),
|
||||
};
|
||||
return;
|
||||
}
|
||||
const GPUVAddr gpu_addr_begin = index_array.StartAddress();
|
||||
const GPUVAddr gpu_addr_end = index_array.EndAddress();
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||
@@ -1491,6 +1512,14 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
|
||||
VAddr end = cpu_addr + wanted_size;
|
||||
int stream_score = 0;
|
||||
bool has_stream_leap = false;
|
||||
if (begin == 0) {
|
||||
return OverlapResult{
|
||||
.ids = std::move(overlap_ids),
|
||||
.begin = begin,
|
||||
.end = end,
|
||||
.has_stream_leap = has_stream_leap,
|
||||
};
|
||||
}
|
||||
for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE);
|
||||
cpu_addr += YUZU_PAGESIZE) {
|
||||
const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS];
|
||||
@@ -1713,12 +1742,12 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
|
||||
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
|
||||
|
||||
if constexpr (USE_MEMORY_MAPS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = 0,
|
||||
.src_offset = upload_staging.offset,
|
||||
.dst_offset = buffer.Offset(dest_address),
|
||||
.size = copy_size,
|
||||
}};
|
||||
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
|
||||
u8* const src_pointer = upload_staging.mapped_span.data();
|
||||
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
|
||||
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
|
||||
|
||||
@@ -20,7 +20,7 @@ void ChannelState::Init(Core::System& system, GPU& gpu) {
|
||||
ASSERT(memory_manager);
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(system, gpu, *memory_manager, *this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
|
||||
kepler_compute = std::make_unique<Engines::KeplerCompute>(system, *memory_manager);
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
|
||||
|
||||
@@ -51,11 +51,11 @@ void State::ProcessData(std::span<const u8> read_buffer) {
|
||||
} else {
|
||||
for (u32 line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr dest_line = address + static_cast<size_t>(line) * regs.dest.pitch;
|
||||
memory_manager.WriteBlockUnsafe(
|
||||
dest_line, read_buffer.data() + static_cast<size_t>(line) * regs.line_length_in,
|
||||
regs.line_length_in);
|
||||
std::span<const u8> buffer(read_buffer.data() +
|
||||
static_cast<size_t>(line) * regs.line_length_in,
|
||||
regs.line_length_in);
|
||||
rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
|
||||
}
|
||||
memory_manager.InvalidateRegion(address, regs.dest.pitch * regs.line_count);
|
||||
}
|
||||
} else {
|
||||
u32 width = regs.dest.width;
|
||||
|
||||
@@ -3,17 +3,25 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/engines/sw_blitter/blitter.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
MICROPROFILE_DECLARE(GPU_BlitEngine);
|
||||
MICROPROFILE_DEFINE(GPU_BlitEngine, "GPU", "Blit Engine", MP_RGB(224, 224, 128));
|
||||
|
||||
using VideoCore::Surface::BytesPerBlock;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
Fermi2D::Fermi2D() {
|
||||
using namespace Texture;
|
||||
|
||||
Fermi2D::Fermi2D(MemoryManager& memory_manager_) {
|
||||
sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_);
|
||||
// Nvidia's OpenGL driver seems to assume these values
|
||||
regs.src.depth = 1;
|
||||
regs.dst.depth = 1;
|
||||
@@ -42,6 +50,7 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
|
||||
}
|
||||
|
||||
void Fermi2D::Blit() {
|
||||
MICROPROFILE_SCOPE(GPU_BlitEngine);
|
||||
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
|
||||
regs.src.Address(), regs.dst.Address());
|
||||
|
||||
@@ -52,9 +61,16 @@ void Fermi2D::Blit() {
|
||||
UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
|
||||
|
||||
const auto& args = regs.pixels_from_memory;
|
||||
constexpr s64 null_derivate = 1ULL << 32;
|
||||
Surface src = regs.src;
|
||||
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
|
||||
src.format != regs.dst.format;
|
||||
Config config{
|
||||
.operation = regs.operation,
|
||||
.filter = args.sample_mode.filter,
|
||||
.must_accelerate =
|
||||
args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu,
|
||||
.dst_x0 = args.dst_x0,
|
||||
.dst_y0 = args.dst_y0,
|
||||
.dst_x1 = args.dst_x0 + args.dst_width,
|
||||
@@ -64,8 +80,7 @@ void Fermi2D::Blit() {
|
||||
.src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
|
||||
.src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
|
||||
};
|
||||
Surface src = regs.src;
|
||||
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
|
||||
const auto need_align_to_pitch =
|
||||
src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch &&
|
||||
static_cast<s32>(src.width) == config.src_x1 &&
|
||||
@@ -78,8 +93,9 @@ void Fermi2D::Blit() {
|
||||
config.src_x1 -= config.src_x0;
|
||||
config.src_x0 = 0;
|
||||
}
|
||||
|
||||
if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
|
||||
UNIMPLEMENTED();
|
||||
sw_blitter->Blit(src, regs.dst, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -21,6 +22,10 @@ class RasterizerInterface;
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
namespace Blitter {
|
||||
class SoftwareBlitEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Engine is known as G80_2D. Documentation can be found in:
|
||||
* https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml
|
||||
@@ -32,7 +37,7 @@ namespace Tegra::Engines {
|
||||
|
||||
class Fermi2D final : public EngineInterface {
|
||||
public:
|
||||
explicit Fermi2D();
|
||||
explicit Fermi2D(MemoryManager& memory_manager_);
|
||||
~Fermi2D() override;
|
||||
|
||||
/// Binds a rasterizer to this engine.
|
||||
@@ -286,6 +291,7 @@ public:
|
||||
struct Config {
|
||||
Operation operation;
|
||||
Filter filter;
|
||||
bool must_accelerate;
|
||||
s32 dst_x0;
|
||||
s32 dst_y0;
|
||||
s32 dst_x1;
|
||||
@@ -298,6 +304,7 @@ public:
|
||||
|
||||
private:
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter;
|
||||
|
||||
/// Performs the copy from the source surface to the destination surface as configured in the
|
||||
/// registers.
|
||||
|
||||
@@ -126,6 +126,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
|
||||
draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
|
||||
draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
|
||||
draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
|
||||
@@ -232,7 +233,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
use_topology_override = true;
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(clear_surface):
|
||||
return ProcessClearBuffers();
|
||||
return ProcessClearBuffers(1);
|
||||
case MAXWELL3D_REG_INDEX(report_semaphore.query):
|
||||
return ProcessQueryGet();
|
||||
case MAXWELL3D_REG_INDEX(render_enable.mode):
|
||||
@@ -249,6 +250,8 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(fragment_barrier):
|
||||
return rasterizer->FragmentBarrier();
|
||||
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
|
||||
return rasterizer->TiledCacheBarrier();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,31 +286,58 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
const u32 argument = ProcessShadowRam(method, method_argument);
|
||||
ProcessDirtyRegisters(method, argument);
|
||||
|
||||
if (draw_command[method]) {
|
||||
regs.reg_array[method] = method_argument;
|
||||
deferred_draw_method.push_back(method);
|
||||
auto u32_to_u8 = [&](const u32 argument) {
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
|
||||
auto update_inline_index = [&](const u32 index) {
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
|
||||
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
|
||||
draw_mode = DrawMode::InlineIndex;
|
||||
};
|
||||
if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
|
||||
u32_to_u8(method_argument);
|
||||
} else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
|
||||
u32_to_u8(regs.inline_index_2x16.even);
|
||||
u32_to_u8(regs.inline_index_2x16.odd);
|
||||
} else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
|
||||
u32_to_u8(regs.inline_index_4x8.index0);
|
||||
u32_to_u8(regs.inline_index_4x8.index1);
|
||||
u32_to_u8(regs.inline_index_4x8.index2);
|
||||
u32_to_u8(regs.inline_index_4x8.index3);
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(draw.end):
|
||||
switch (draw_mode) {
|
||||
case DrawMode::General:
|
||||
ProcessDraw(1);
|
||||
break;
|
||||
case DrawMode::InlineIndex:
|
||||
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
|
||||
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
|
||||
ProcessDraw(1);
|
||||
inline_index_draw_indexes.clear();
|
||||
break;
|
||||
case DrawMode::Instance:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(draw_inline_index):
|
||||
update_inline_index(method_argument);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
|
||||
update_inline_index(regs.inline_index_2x16.even);
|
||||
update_inline_index(regs.inline_index_2x16.odd);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
|
||||
update_inline_index(regs.inline_index_4x8.index0);
|
||||
update_inline_index(regs.inline_index_4x8.index1);
|
||||
update_inline_index(regs.inline_index_4x8.index2);
|
||||
update_inline_index(regs.inline_index_4x8.index3);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(draw.instance_id):
|
||||
draw_mode =
|
||||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
|
||||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
|
||||
? DrawMode::Instance
|
||||
: DrawMode::General;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ProcessDeferredDraw();
|
||||
|
||||
const u32 argument = ProcessShadowRam(method, method_argument);
|
||||
ProcessDirtyRegisters(method, argument);
|
||||
ProcessMethodCall(method, argument, method_argument, is_last_call);
|
||||
}
|
||||
}
|
||||
@@ -506,10 +536,7 @@ void Maxwell3D::ProcessCounterReset() {
|
||||
|
||||
void Maxwell3D::ProcessSyncPoint() {
|
||||
const u32 sync_point = regs.sync_info.sync_point.Value();
|
||||
const u32 cache_flush = regs.sync_info.clean_l2.Value();
|
||||
if (cache_flush != 0) {
|
||||
rasterizer->InvalidateGPUCache();
|
||||
}
|
||||
[[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value();
|
||||
rasterizer->SignalSyncPoint(sync_point);
|
||||
}
|
||||
|
||||
@@ -591,8 +618,8 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
|
||||
return regs.reg_array[method];
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessClearBuffers() {
|
||||
rasterizer->Clear();
|
||||
void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
|
||||
rasterizer->Clear(layer_count);
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessDraw(u32 instance_count) {
|
||||
@@ -621,57 +648,27 @@ void Maxwell3D::ProcessDraw(u32 instance_count) {
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessDeferredDraw() {
|
||||
if (deferred_draw_method.empty()) {
|
||||
if (draw_mode != DrawMode::Instance || deferred_draw_method.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
enum class DrawMode {
|
||||
Undefined,
|
||||
General,
|
||||
Instance,
|
||||
};
|
||||
DrawMode draw_mode{DrawMode::Undefined};
|
||||
u32 method_count = static_cast<u32>(deferred_draw_method.size());
|
||||
u32 method = deferred_draw_method[method_count - 1];
|
||||
if (MAXWELL3D_REG_INDEX(draw.end) != method) {
|
||||
return;
|
||||
}
|
||||
draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
|
||||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
|
||||
? DrawMode::Instance
|
||||
: DrawMode::General;
|
||||
u32 instance_count = 0;
|
||||
if (draw_mode == DrawMode::Instance) {
|
||||
u32 vertex_buffer_count = 0;
|
||||
u32 index_buffer_count = 0;
|
||||
for (u32 index = 0; index < method_count; ++index) {
|
||||
method = deferred_draw_method[index];
|
||||
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
|
||||
instance_count = ++vertex_buffer_count;
|
||||
} else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
|
||||
instance_count = ++index_buffer_count;
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(!(vertex_buffer_count && index_buffer_count),
|
||||
"Instance both indexed and direct?");
|
||||
} else {
|
||||
instance_count = 1;
|
||||
for (u32 index = 0; index < method_count; ++index) {
|
||||
method = deferred_draw_method[index];
|
||||
if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
|
||||
MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
|
||||
MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
|
||||
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
|
||||
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
|
||||
break;
|
||||
}
|
||||
u32 instance_count = 1;
|
||||
u32 vertex_buffer_count = 0;
|
||||
u32 index_buffer_count = 0;
|
||||
for (u32 index = 0; index < method_count; ++index) {
|
||||
u32 method = deferred_draw_method[index];
|
||||
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
|
||||
instance_count = ++vertex_buffer_count;
|
||||
} else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
|
||||
instance_count = ++index_buffer_count;
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
|
||||
|
||||
ProcessDraw(instance_count);
|
||||
|
||||
deferred_draw_method.clear();
|
||||
inline_index_draw_indexes.clear();
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -707,7 +707,7 @@ public:
|
||||
case Size::Size_A2_B10_G10_R10:
|
||||
return "2_10_10_10";
|
||||
case Size::Size_B10_G11_R11:
|
||||
return "10_11_12";
|
||||
return "10_11_11";
|
||||
default:
|
||||
ASSERT(false);
|
||||
return {};
|
||||
@@ -2639,7 +2639,7 @@ public:
|
||||
L2CacheControl l2_cache_control; ///< 0x0218
|
||||
InvalidateShaderCache invalidate_shader_cache; ///< 0x021C
|
||||
INSERT_PADDING_BYTES_NOINIT(0xA8);
|
||||
SyncInfo sync_info; ///< 0x02C8
|
||||
SyncInfo sync_info; ///< 0x02C8
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
u32 prim_circular_buffer_throttle; ///< 0x02D0
|
||||
u32 flush_invalidate_rop_mini_cache; ///< 0x02D4
|
||||
@@ -2731,7 +2731,11 @@ public:
|
||||
s32 stencil_back_ref; ///< 0x0F54
|
||||
u32 stencil_back_mask; ///< 0x0F58
|
||||
u32 stencil_back_func_mask; ///< 0x0F5C
|
||||
INSERT_PADDING_BYTES_NOINIT(0x24);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x14);
|
||||
u32 invalidate_texture_data_cache; ///< 0x0F74 Assumed - Not in official docs.
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
u32 tiled_cache_barrier; ///< 0x0F7C Assumed - Not in official docs.
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
VertexStreamSubstitute vertex_stream_substitute; ///< 0x0F84
|
||||
u32 line_mode_clip_generated_edge_do_not_draw; ///< 0x0F8C
|
||||
u32 color_mask_common; ///< 0x0F90
|
||||
@@ -2791,7 +2795,8 @@ public:
|
||||
FillViaTriangleMode fill_via_triangle_mode; ///< 0x113C
|
||||
u32 blend_per_format_snorm8_unorm16_snorm16_enabled; ///< 0x1140
|
||||
u32 flush_pending_writes_sm_gloal_store; ///< 0x1144
|
||||
INSERT_PADDING_BYTES_NOINIT(0x18);
|
||||
u32 conservative_raster_enable; ///< 0x1148 Assumed - Not in official docs.
|
||||
INSERT_PADDING_BYTES_NOINIT(0x14);
|
||||
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; ///< 0x1160
|
||||
std::array<MsaaSampleLocation, 4> multisample_sample_locations; ///< 0x11E0
|
||||
u32 offset_render_target_index_by_viewport_index; ///< 0x11F0
|
||||
@@ -3081,6 +3086,9 @@ public:
|
||||
|
||||
std::vector<u8> inline_index_draw_indexes;
|
||||
|
||||
/// Handles a write to the CLEAR_BUFFERS register.
|
||||
void ProcessClearBuffers(u32 layer_count);
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
@@ -3115,9 +3123,6 @@ private:
|
||||
/// Handles firmware blob 4
|
||||
void ProcessFirmwareCall4();
|
||||
|
||||
/// Handles a write to the CLEAR_BUFFERS register.
|
||||
void ProcessClearBuffers();
|
||||
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
@@ -3143,10 +3148,12 @@ private:
|
||||
/// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
|
||||
void ProcessTopologyOverride();
|
||||
|
||||
void ProcessDraw(u32 instance_count = 1);
|
||||
|
||||
/// Handles deferred draw(e.g., instance draw).
|
||||
void ProcessDeferredDraw();
|
||||
|
||||
/// Handles a draw.
|
||||
void ProcessDraw(u32 instance_count = 1);
|
||||
|
||||
/// Returns a query's value or an empty object if the value will be deferred through a cache.
|
||||
std::optional<u64> GetQueryResult();
|
||||
|
||||
@@ -3173,6 +3180,8 @@ private:
|
||||
|
||||
std::array<bool, Regs::NUM_REGS> draw_command{};
|
||||
std::vector<u32> deferred_draw_method;
|
||||
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
|
||||
DrawMode draw_mode{DrawMode::General};
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
@@ -3287,6 +3296,8 @@ ASSERT_REG_POSITION(const_color_rendering, 0x0F40);
|
||||
ASSERT_REG_POSITION(stencil_back_ref, 0x0F54);
|
||||
ASSERT_REG_POSITION(stencil_back_mask, 0x0F58);
|
||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x0F5C);
|
||||
ASSERT_REG_POSITION(invalidate_texture_data_cache, 0x0F74);
|
||||
ASSERT_REG_POSITION(tiled_cache_barrier, 0x0F7C);
|
||||
ASSERT_REG_POSITION(vertex_stream_substitute, 0x0F84);
|
||||
ASSERT_REG_POSITION(line_mode_clip_generated_edge_do_not_draw, 0x0F8C);
|
||||
ASSERT_REG_POSITION(color_mask_common, 0x0F90);
|
||||
@@ -3343,6 +3354,7 @@ ASSERT_REG_POSITION(post_ps_use_pre_ps_coverage, 0x1138);
|
||||
ASSERT_REG_POSITION(fill_via_triangle_mode, 0x113C);
|
||||
ASSERT_REG_POSITION(blend_per_format_snorm8_unorm16_snorm16_enabled, 0x1140);
|
||||
ASSERT_REG_POSITION(flush_pending_writes_sm_gloal_store, 0x1144);
|
||||
ASSERT_REG_POSITION(conservative_raster_enable, 0x1148);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x1160);
|
||||
ASSERT_REG_POSITION(multisample_sample_locations, 0x11E0);
|
||||
ASSERT_REG_POSITION(offset_render_target_index_by_viewport_index, 0x11F0);
|
||||
|
||||
@@ -62,7 +62,8 @@ void MaxwellDMA::Launch() {
|
||||
|
||||
if (!is_src_pitch && !is_dst_pitch) {
|
||||
// If both the source and the destination are in block layout, assert.
|
||||
UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
|
||||
CopyBlockLinearToBlockLinear();
|
||||
ReleaseSemaphore();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,26 +103,29 @@ void MaxwellDMA::Launch() {
|
||||
const bool is_src_pitch = IsPitchKind(static_cast<PTEKind>(src_kind));
|
||||
const bool is_dst_pitch = IsPitchKind(static_cast<PTEKind>(dst_kind));
|
||||
if (!is_src_pitch && is_dst_pitch) {
|
||||
std::vector<u8> tmp_buffer(regs.line_length_in);
|
||||
std::vector<u8> dst_buffer(regs.line_length_in);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
|
||||
regs.line_length_in);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
|
||||
dst_buffer[offset] =
|
||||
tmp_buffer[convert_linear_2_blocklinear_addr(regs.offset_in + offset) -
|
||||
regs.offset_in];
|
||||
UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
|
||||
UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
|
||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||
std::vector<u8> tmp_buffer(16);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||
memory_manager.ReadBlockUnsafe(
|
||||
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
|
||||
tmp_buffer.data(), tmp_buffer.size());
|
||||
memory_manager.WriteBlock(regs.offset_out + offset, tmp_buffer.data(),
|
||||
tmp_buffer.size());
|
||||
}
|
||||
memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
|
||||
} else if (is_src_pitch && !is_dst_pitch) {
|
||||
std::vector<u8> tmp_buffer(regs.line_length_in);
|
||||
std::vector<u8> dst_buffer(regs.line_length_in);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
|
||||
regs.line_length_in);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; ++offset) {
|
||||
dst_buffer[convert_linear_2_blocklinear_addr(regs.offset_out + offset) -
|
||||
regs.offset_out] = tmp_buffer[offset];
|
||||
UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
|
||||
UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
|
||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||
std::vector<u8> tmp_buffer(16);
|
||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
|
||||
tmp_buffer.size());
|
||||
memory_manager.WriteBlock(
|
||||
convert_linear_2_blocklinear_addr(regs.offset_out + offset),
|
||||
tmp_buffer.data(), tmp_buffer.size());
|
||||
}
|
||||
memory_manager.WriteBlock(regs.offset_out, dst_buffer.data(), regs.line_length_in);
|
||||
} else {
|
||||
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
|
||||
std::vector<u8> tmp_buffer(regs.line_length_in);
|
||||
@@ -288,6 +292,70 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyBlockLinearToBlockLinear() {
|
||||
UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
|
||||
|
||||
const bool is_remapping = regs.launch_dma.remap_enable != 0;
|
||||
|
||||
// Deswizzle the input and copy it over.
|
||||
const Parameters& src = regs.src_params;
|
||||
const Parameters& dst = regs.dst_params;
|
||||
|
||||
const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
|
||||
const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
|
||||
|
||||
const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
|
||||
|
||||
u32 src_width = src.width;
|
||||
u32 dst_width = dst.width;
|
||||
u32 x_elements = regs.line_length_in;
|
||||
u32 src_x_offset = src.origin.x;
|
||||
u32 dst_x_offset = dst.origin.x;
|
||||
u32 bpp_shift = 0U;
|
||||
if (!is_remapping) {
|
||||
bpp_shift = Common::FoldRight(
|
||||
4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
|
||||
src_width, dst_width, x_elements, src_x_offset, dst_x_offset,
|
||||
static_cast<u32>(regs.offset_in), static_cast<u32>(regs.offset_out));
|
||||
src_width >>= bpp_shift;
|
||||
dst_width >>= bpp_shift;
|
||||
x_elements >>= bpp_shift;
|
||||
src_x_offset >>= bpp_shift;
|
||||
dst_x_offset >>= bpp_shift;
|
||||
}
|
||||
|
||||
const u32 bytes_per_pixel = base_bpp << bpp_shift;
|
||||
const size_t src_size = CalculateSize(true, bytes_per_pixel, src_width, src.height, src.depth,
|
||||
src.block_size.height, src.block_size.depth);
|
||||
const size_t dst_size = CalculateSize(true, bytes_per_pixel, dst_width, dst.height, dst.depth,
|
||||
dst.block_size.height, dst.block_size.depth);
|
||||
|
||||
const u32 pitch = x_elements * bytes_per_pixel;
|
||||
const size_t mid_buffer_size = pitch * regs.line_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
intermediate_buffer.resize(mid_buffer_size);
|
||||
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
|
||||
UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height,
|
||||
src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
|
||||
src.block_size.height, src.block_size.depth, pitch);
|
||||
|
||||
SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
|
||||
dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
|
||||
dst.block_size.height, dst.block_size.depth, pitch);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::ReleaseSemaphore() {
|
||||
const auto type = regs.launch_dma.semaphore_type;
|
||||
const GPUVAddr address = regs.semaphore.address;
|
||||
@@ -311,6 +379,7 @@ void MaxwellDMA::ReleaseSemaphore() {
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,8 @@ private:
|
||||
|
||||
void CopyPitchToBlockLinear();
|
||||
|
||||
void CopyBlockLinearToBlockLinear();
|
||||
|
||||
void FastCopyBlockLinearToPitch();
|
||||
|
||||
void ReleaseSemaphore();
|
||||
@@ -234,6 +236,7 @@ private:
|
||||
|
||||
std::vector<u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
std::vector<u8> intermediate_buffer;
|
||||
|
||||
static constexpr std::size_t NUM_REGS = 0x800;
|
||||
struct Regs {
|
||||
|
||||
@@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ void Puller::ProcessSemaphoreRelease() {
|
||||
std::function<void()> operation([this, sequence_address, payload] {
|
||||
memory_manager.Write<u32>(sequence_address, payload);
|
||||
});
|
||||
rasterizer->SyncOperation(std::move(operation));
|
||||
rasterizer->SignalFence(std::move(operation));
|
||||
}
|
||||
|
||||
void Puller::ProcessSemaphoreAcquire() {
|
||||
@@ -149,8 +151,8 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
|
||||
case BufferMethods::SemaphoreAddressLow:
|
||||
case BufferMethods::SemaphoreSequencePayload:
|
||||
case BufferMethods::SyncpointPayload:
|
||||
break;
|
||||
case BufferMethods::WrcacheFlush:
|
||||
break;
|
||||
case BufferMethods::RefCnt:
|
||||
rasterizer->SignalReference();
|
||||
break;
|
||||
@@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
238
src/video_core/engines/sw_blitter/blitter.cpp
Normal file
238
src/video_core/engines/sw_blitter/blitter.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/engines/sw_blitter/blitter.h"
|
||||
#include "video_core/engines/sw_blitter/converter.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
using VideoCore::Surface::BytesPerBlock;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
|
||||
namespace Tegra::Engines::Blitter {
|
||||
|
||||
using namespace Texture;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t ir_components = 4;
|
||||
|
||||
void NearestNeighbor(std::span<const u8> input, std::span<u8> output, u32 src_width, u32 src_height,
|
||||
u32 dst_width, u32 dst_height, size_t bpp) {
|
||||
const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
|
||||
const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
|
||||
size_t src_y = 0;
|
||||
for (u32 y = 0; y < dst_height; y++) {
|
||||
size_t src_x = 0;
|
||||
for (u32 x = 0; x < dst_width; x++) {
|
||||
const size_t read_from = ((src_y * src_width + src_x) >> 32) * bpp;
|
||||
const size_t write_to = (y * dst_width + x) * bpp;
|
||||
|
||||
std::memcpy(&output[write_to], &input[read_from], bpp);
|
||||
src_x += dx_du;
|
||||
}
|
||||
src_y += dy_dv;
|
||||
}
|
||||
}
|
||||
|
||||
void NearestNeighborFast(std::span<const f32> input, std::span<f32> output, u32 src_width,
|
||||
u32 src_height, u32 dst_width, u32 dst_height) {
|
||||
const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
|
||||
const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
|
||||
size_t src_y = 0;
|
||||
for (u32 y = 0; y < dst_height; y++) {
|
||||
size_t src_x = 0;
|
||||
for (u32 x = 0; x < dst_width; x++) {
|
||||
const size_t read_from = ((src_y * src_width + src_x) >> 32) * ir_components;
|
||||
const size_t write_to = (y * dst_width + x) * ir_components;
|
||||
|
||||
std::memcpy(&output[write_to], &input[read_from], sizeof(f32) * ir_components);
|
||||
src_x += dx_du;
|
||||
}
|
||||
src_y += dy_dv;
|
||||
}
|
||||
}
|
||||
|
||||
void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_width,
|
||||
size_t src_height, size_t dst_width, size_t dst_height) {
|
||||
const auto bilinear_sample = [](std::span<const f32> x0_y0, std::span<const f32> x1_y0,
|
||||
std::span<const f32> x0_y1, std::span<const f32> x1_y1,
|
||||
f32 weight_x, f32 weight_y) {
|
||||
std::array<f32, ir_components> result{};
|
||||
for (size_t i = 0; i < ir_components; i++) {
|
||||
const f32 a = std::lerp(x0_y0[i], x1_y0[i], weight_x);
|
||||
const f32 b = std::lerp(x0_y1[i], x1_y1[i], weight_x);
|
||||
result[i] = std::lerp(a, b, weight_y);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const f32 dx_du =
|
||||
dst_width > 1 ? static_cast<f32>(src_width - 1) / static_cast<f32>(dst_width - 1) : 0.f;
|
||||
const f32 dy_dv =
|
||||
dst_height > 1 ? static_cast<f32>(src_height - 1) / static_cast<f32>(dst_height - 1) : 0.f;
|
||||
for (u32 y = 0; y < dst_height; y++) {
|
||||
for (u32 x = 0; x < dst_width; x++) {
|
||||
const f32 x_low = std::floor(static_cast<f32>(x) * dx_du);
|
||||
const f32 y_low = std::floor(static_cast<f32>(y) * dy_dv);
|
||||
const f32 x_high = std::ceil(static_cast<f32>(x) * dx_du);
|
||||
const f32 y_high = std::ceil(static_cast<f32>(y) * dy_dv);
|
||||
const f32 weight_x = (static_cast<f32>(x) * dx_du) - x_low;
|
||||
const f32 weight_y = (static_cast<f32>(y) * dy_dv) - y_low;
|
||||
|
||||
const auto read_src = [&](f32 in_x, f32 in_y) {
|
||||
const size_t read_from =
|
||||
((static_cast<size_t>(in_x) * src_width + static_cast<size_t>(in_y)) >> 32) *
|
||||
ir_components;
|
||||
return std::span<const f32>(&input[read_from], ir_components);
|
||||
};
|
||||
|
||||
auto x0_y0 = read_src(x_low, y_low);
|
||||
auto x1_y0 = read_src(x_high, y_low);
|
||||
auto x0_y1 = read_src(x_low, y_high);
|
||||
auto x1_y1 = read_src(x_high, y_high);
|
||||
|
||||
const auto result = bilinear_sample(x0_y0, x1_y0, x0_y1, x1_y1, weight_x, weight_y);
|
||||
|
||||
const size_t write_to = (y * dst_width + x) * ir_components;
|
||||
|
||||
std::memcpy(&output[write_to], &result, sizeof(f32) * ir_components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct SoftwareBlitEngine::BlitEngineImpl {
|
||||
std::vector<u8> tmp_buffer;
|
||||
std::vector<u8> src_buffer;
|
||||
std::vector<u8> dst_buffer;
|
||||
std::vector<f32> intermediate_src;
|
||||
std::vector<f32> intermediate_dst;
|
||||
ConverterFactory converter_factory;
|
||||
};
|
||||
|
||||
SoftwareBlitEngine::SoftwareBlitEngine(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_} {
|
||||
impl = std::make_unique<BlitEngineImpl>();
|
||||
}
|
||||
|
||||
SoftwareBlitEngine::~SoftwareBlitEngine() = default;
|
||||
|
||||
bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
|
||||
Fermi2D::Config& config) {
|
||||
const auto get_surface_size = [](Fermi2D::Surface& surface, u32 bytes_per_pixel) {
|
||||
if (surface.linear == Fermi2D::MemoryLayout::BlockLinear) {
|
||||
return CalculateSize(true, bytes_per_pixel, surface.width, surface.height,
|
||||
surface.depth, surface.block_height, surface.block_depth);
|
||||
}
|
||||
return static_cast<size_t>(surface.pitch * surface.height);
|
||||
};
|
||||
const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
|
||||
std::span<u8> output, u32 extent_x, u32 extent_y,
|
||||
u32 pitch, u32 x0, u32 y0, size_t bpp) {
|
||||
const size_t base_offset = x0 * bpp;
|
||||
const size_t copy_size = extent_x * bpp;
|
||||
for (u32 y = y0; y < extent_y; y++) {
|
||||
const size_t first_offset = y * pitch + base_offset;
|
||||
const size_t second_offset = y * extent_x * bpp;
|
||||
u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
|
||||
const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
|
||||
std::memcpy(write_to, read_from, copy_size);
|
||||
}
|
||||
};
|
||||
|
||||
const u32 src_extent_x = config.src_x1 - config.src_x0;
|
||||
const u32 src_extent_y = config.src_y1 - config.src_y0;
|
||||
|
||||
const u32 dst_extent_x = config.dst_x1 - config.dst_x0;
|
||||
const u32 dst_extent_y = config.dst_y1 - config.dst_y0;
|
||||
const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
|
||||
const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
|
||||
impl->tmp_buffer.resize(src_size);
|
||||
memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
|
||||
|
||||
const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
|
||||
|
||||
const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
|
||||
|
||||
impl->src_buffer.resize(src_copy_size);
|
||||
|
||||
const bool no_passthrough =
|
||||
src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y;
|
||||
|
||||
const auto convertion_phase_same_format = [&]() {
|
||||
NearestNeighbor(impl->src_buffer, impl->dst_buffer, src_extent_x, src_extent_y,
|
||||
dst_extent_x, dst_extent_y, dst_bytes_per_pixel);
|
||||
};
|
||||
|
||||
const auto convertion_phase_ir = [&]() {
|
||||
auto* input_converter = impl->converter_factory.GetFormatConverter(src.format);
|
||||
impl->intermediate_src.resize((src_copy_size / src_bytes_per_pixel) * ir_components);
|
||||
impl->intermediate_dst.resize((dst_copy_size / dst_bytes_per_pixel) * ir_components);
|
||||
input_converter->ConvertTo(impl->src_buffer, impl->intermediate_src);
|
||||
|
||||
if (config.filter != Fermi2D::Filter::Bilinear) {
|
||||
NearestNeighborFast(impl->intermediate_src, impl->intermediate_dst, src_extent_x,
|
||||
src_extent_y, dst_extent_x, dst_extent_y);
|
||||
} else {
|
||||
Bilinear(impl->intermediate_src, impl->intermediate_dst, src_extent_x, src_extent_y,
|
||||
dst_extent_x, dst_extent_y);
|
||||
}
|
||||
|
||||
auto* output_converter = impl->converter_factory.GetFormatConverter(dst.format);
|
||||
output_converter->ConvertFrom(impl->intermediate_dst, impl->dst_buffer);
|
||||
};
|
||||
|
||||
// Do actuall Blit
|
||||
|
||||
impl->dst_buffer.resize(dst_copy_size);
|
||||
if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
|
||||
UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width,
|
||||
src.height, src.depth, config.src_x0, config.src_y0, src_extent_x,
|
||||
src_extent_y, src.block_height, src.block_depth,
|
||||
src_extent_x * src_bytes_per_pixel);
|
||||
} else {
|
||||
process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
|
||||
src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
|
||||
}
|
||||
|
||||
// Conversion Phase
|
||||
if (no_passthrough) {
|
||||
if (src.format != dst.format || config.filter == Fermi2D::Filter::Bilinear) {
|
||||
convertion_phase_ir();
|
||||
} else {
|
||||
convertion_phase_same_format();
|
||||
}
|
||||
} else {
|
||||
impl->dst_buffer.swap(impl->src_buffer);
|
||||
}
|
||||
|
||||
const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
|
||||
impl->tmp_buffer.resize(dst_size);
|
||||
memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
|
||||
|
||||
if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {
|
||||
SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width,
|
||||
dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x,
|
||||
dst_extent_y, dst.block_height, dst.block_depth,
|
||||
dst_extent_x * dst_bytes_per_pixel);
|
||||
} else {
|
||||
process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y,
|
||||
dst.pitch, config.dst_x0, config.dst_y0,
|
||||
static_cast<size_t>(dst_bytes_per_pixel));
|
||||
}
|
||||
memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines::Blitter
|
||||
27
src/video_core/engines/sw_blitter/blitter.h
Normal file
27
src/video_core/engines/sw_blitter/blitter.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines::Blitter {
|
||||
|
||||
class SoftwareBlitEngine {
|
||||
public:
|
||||
explicit SoftwareBlitEngine(MemoryManager& memory_manager_);
|
||||
~SoftwareBlitEngine();
|
||||
|
||||
bool Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, Fermi2D::Config& copy_config);
|
||||
|
||||
private:
|
||||
MemoryManager& memory_manager;
|
||||
struct BlitEngineImpl;
|
||||
std::unique_ptr<BlitEngineImpl> impl;
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines::Blitter
|
||||
1234
src/video_core/engines/sw_blitter/converter.cpp
Normal file
1234
src/video_core/engines/sw_blitter/converter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
36
src/video_core/engines/sw_blitter/converter.h
Normal file
36
src/video_core/engines/sw_blitter/converter.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra::Engines::Blitter {
|
||||
|
||||
class Converter {
|
||||
public:
|
||||
virtual void ConvertTo(std::span<const u8> input, std::span<f32> output) = 0;
|
||||
virtual void ConvertFrom(std::span<const f32> input, std::span<u8> output) = 0;
|
||||
virtual ~Converter() = default;
|
||||
};
|
||||
|
||||
class ConverterFactory {
|
||||
public:
|
||||
ConverterFactory();
|
||||
~ConverterFactory();
|
||||
|
||||
Converter* GetFormatConverter(RenderTargetFormat format);
|
||||
|
||||
private:
|
||||
Converter* BuildConverter(RenderTargetFormat format);
|
||||
|
||||
struct ConverterFactoryImpl;
|
||||
std::unique_ptr<ConverterFactoryImpl> impl;
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines::Blitter
|
||||
136
src/video_core/engines/sw_blitter/generate_converters.py
Normal file
136
src/video_core/engines/sw_blitter/generate_converters.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import re
|
||||
|
||||
class Format:
|
||||
def __init__(self, string_value):
|
||||
self.name = string_value
|
||||
tmp = string_value.split('_')
|
||||
self.component_type = tmp[1]
|
||||
component_data = re.findall(r"\w\d+", tmp[0])
|
||||
self.num_components = len(component_data)
|
||||
sizes = []
|
||||
swizzle = []
|
||||
for data in component_data:
|
||||
swizzle.append(data[0])
|
||||
sizes.append(int(data[1:]))
|
||||
self.sizes = sizes
|
||||
self.swizzle = swizzle
|
||||
|
||||
def build_component_type_array(self):
|
||||
result = "{ "
|
||||
b = False
|
||||
for i in range(0, self.num_components):
|
||||
if b:
|
||||
result += ", "
|
||||
b = True
|
||||
result += "ComponentType::" + self.component_type
|
||||
result += " }"
|
||||
return result
|
||||
|
||||
def build_component_sizes_array(self):
|
||||
result = "{ "
|
||||
b = False
|
||||
for i in range(0, self.num_components):
|
||||
if b:
|
||||
result += ", "
|
||||
b = True
|
||||
result += str(self.sizes[i])
|
||||
result += " }"
|
||||
return result
|
||||
|
||||
def build_component_swizzle_array(self):
|
||||
result = "{ "
|
||||
b = False
|
||||
for i in range(0, self.num_components):
|
||||
if b:
|
||||
result += ", "
|
||||
b = True
|
||||
swizzle = self.swizzle[i]
|
||||
if swizzle == "X":
|
||||
swizzle = "None"
|
||||
result += "Swizzle::" + swizzle
|
||||
result += " }"
|
||||
return result
|
||||
|
||||
def print_declaration(self):
|
||||
print("struct " + self.name + "Traits {")
|
||||
print(" static constexpr size_t num_components = " + str(self.num_components) + ";")
|
||||
print(" static constexpr std::array<ComponentType, num_components> component_types = " + self.build_component_type_array() + ";")
|
||||
print(" static constexpr std::array<size_t, num_components> component_sizes = " + self.build_component_sizes_array() + ";")
|
||||
print(" static constexpr std::array<Swizzle, num_components> component_swizzle = " + self.build_component_swizzle_array() + ";")
|
||||
print("};\n")
|
||||
|
||||
def print_case(self):
|
||||
print("case RenderTargetFormat::" + self.name + ":")
|
||||
print(" return impl->converters_cache")
|
||||
print(" .emplace(format, std::make_unique<ConverterImpl<" + self.name + "Traits>>())")
|
||||
print(" .first->second.get();")
|
||||
print(" break;")
|
||||
|
||||
txt = """
|
||||
R32G32B32A32_FLOAT
|
||||
R32G32B32A32_SINT
|
||||
R32G32B32A32_UINT
|
||||
R32G32B32X32_FLOAT
|
||||
R32G32B32X32_SINT
|
||||
R32G32B32X32_UINT
|
||||
R16G16B16A16_UNORM
|
||||
R16G16B16A16_SNORM
|
||||
R16G16B16A16_SINT
|
||||
R16G16B16A16_UINT
|
||||
R16G16B16A16_FLOAT
|
||||
R32G32_FLOAT
|
||||
R32G32_SINT
|
||||
R32G32_UINT
|
||||
R16G16B16X16_FLOAT
|
||||
A8R8G8B8_UNORM
|
||||
A8R8G8B8_SRGB
|
||||
A2B10G10R10_UNORM
|
||||
A2B10G10R10_UINT
|
||||
A2R10G10B10_UNORM
|
||||
A8B8G8R8_UNORM
|
||||
A8B8G8R8_SRGB
|
||||
A8B8G8R8_SNORM
|
||||
A8B8G8R8_SINT
|
||||
A8B8G8R8_UINT
|
||||
R16G16_UNORM
|
||||
R16G16_SNORM
|
||||
R16G16_SINT
|
||||
R16G16_UINT
|
||||
R16G16_FLOAT
|
||||
B10G11R11_FLOAT
|
||||
R32_SINT
|
||||
R32_UINT
|
||||
R32_FLOAT
|
||||
X8R8G8B8_UNORM
|
||||
X8R8G8B8_SRGB
|
||||
R5G6B5_UNORM
|
||||
A1R5G5B5_UNORM
|
||||
R8G8_UNORM
|
||||
R8G8_SNORM
|
||||
R8G8_SINT
|
||||
R8G8_UINT
|
||||
R16_UNORM
|
||||
R16_SNORM
|
||||
R16_SINT
|
||||
R16_UINT
|
||||
R16_FLOAT
|
||||
R8_UNORM
|
||||
R8_SNORM
|
||||
R8_SINT
|
||||
R8_UINT
|
||||
X1R5G5B5_UNORM
|
||||
X8B8G8R8_UNORM
|
||||
X8B8G8R8_SRGB
|
||||
"""
|
||||
|
||||
x = txt.split()
|
||||
y = list(map(lambda a: Format(a), x))
|
||||
formats = list(y)
|
||||
for format in formats:
|
||||
format.print_declaration()
|
||||
|
||||
for format in formats:
|
||||
format.print_case()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user