Compare commits

...

15 Commits

Author SHA1 Message Date
Bix 92f54a034a [android, docs] Update recommended drivers, Fix Android build Guide and Fix X link. (#4617)
Nitter has been taken down, Link has been reverted to X.
Updated driver recommended to T30 for Adreno 711- 899 and T29 for 700 - 710.
Fixed Build guide for android via terminal, Due to new android SDK suffox.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [X] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
- [X] I have read and followed the [website policy](https://git.eden-emu.dev/eden-emu/site-policy).
- [X] I affirm that the content of this pull request body was written and submitted entirely by me, in my own words, and not by an AI agent or LLM.
- [ ] I affirm that I did not read any of this, and understand that my pull request will be closed if I check this box.
- [X] I affirm that, by submitting this pull request, I am responsible for all of the changes contained within these commits.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4617
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-02 04:32:10 +01:00
crueter 7a6d055094 [desktop] queue FirstFrameDisplayed to fix loading screen race (#4536)
Signed-off-by: crueter <crueter@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Authored-by: Bitekiyo <334587657+Bitekiyo@users.noreply.github.com>

Co-authored-by: Bitekiyo <334587657+Bitekiyo@users.noreply.github.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4536
2026-11-01 21:53:04 +01:00
lizzie c6fa5c1f77 [common] use handrolled string utility conversions instead of wstring codecvt (#3773)
thanks to @chrelliott978 for the initial impl, basically clang will yell at you if you dare use wstring_codecvt<> and some other shit, and to be fair one day it will be removed because it's deprecated (but it's c++, we know it will NEVER be deprecated)

either way this makes clang shut up

untested at all

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3773
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 21:52:29 +01:00
lizzie 2c40328a23 [macos, net] add sysctl(RT_xx)-fetched network interfaces (#3656)
should fix macOS network interfaces

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Co-Authored-by: @chrelliott978
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3656
Reviewed-by: Ghost <>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-11-01 21:51:51 +01:00
lizzie 41b6807f12 [fs] use mmap() to read files using VM/pagefaults for higher throughput (#3151)
main idea is basically to manually manage the file backed storage ourselves

basically the libc will use pooled workes who will read off sync data and may or may not use mmap, this detail can vary between libcs, so id rather have one unified concept of "paged file" as opposed to relying on the libc doing what it should

the main benefit is that one doesnt need to call fwrite/fread or whatever, they can just do normal paged file ops and such

this pr has seen changes from mapped flags as well, as some OSes; particularly android, do not enjoy being told to make hugepages that are 1GB in size, especially for lower end devices.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3151
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 21:51:15 +01:00
lizzie 2bec66c092 [dynarmic] Remove redundant configure struct (#4485)
Trivial change. Makes translate use the top level user config instead of "grabbing" stuff into a dedicated struct.
This isn't a parent ref because TranslateVisitor is a light scoped struct.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4485
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 21:48:38 +01:00
lizzie be972d7fdc [dynarmic] Remove redundant constant pool ops (#4516)
Simplifies logic for adding constants to pool. Since std::pair<T,U>
is hash-able by boost::hash, and `std::array` (u128) has better cgen than `std::pair`.
I felt inclined to use std::pair for the map and u128 for the span (as later on I plan to
use vector_size(16) to make it unto a proper 128-bit primitive).

Another note is that I noticed a lot of collisions (especially on heavy games).
I used boost::hash as this seemed to reduce the number of them drastically.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4516
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 21:46:19 +01:00
crueter 972860ac8f [dist] reoptimize assets, and add svg optimization (#4537)
Co-authored-by: GreenStar <145512144+thegreatGreenstar@users.noreply.github.com>
Signed-off-by: crueter <crueter@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4537
2026-11-01 21:45:45 +01:00
PavelBARABANOV 3d3ed4b7d1 [shader_recompiler, renderer_vulkan] draw sprites whose texture handle comes from a vertex attribute (#4566)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
credit: Ghael-V

Draw sprites whose texture handle comes from a vertex attribute. A bindless handle read from an interpolated vertex attribute could not be tracked to a constant buffer, so pipeline creation threw and every draw using the shader was dropped (FIFA player indicators, shadows and some text).

The handle now gets a sentinel descriptor, and the Vulkan rasterizer splits a triangle-list draw into runs of vertices that share a handle and binds each run's texture before drawing it.

https://github.com/Ghael-V/Lemon-Project/commit/4fec247e9eb29a6e81a51d8252323c5cda6b2cb9
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4566
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-11-01 21:44:40 +01:00
project516 be21ccce25 [docs] fix link to AI and LLM Usage in docs/README.md (#4618)
docs/policies/AI.md was removed in a2aa4d97e1.

I linked it to the policies section in CONTRIBUTING.md, since AI and LLM usage is important to have. We can just remove it though.

Signed-off-by: project516 <project516@project516.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
- [x] I have read and followed the [website policy](https://git.eden-emu.dev/eden-emu/site-policy).
- [x] I affirm that the content of this pull request body was written and submitted entirely by me, in my own words, and not by an AI agent or LLM.
- [ ] I affirm that I did not read any of this, and understand that my pull request will be closed if I check this box.
- [x] I affirm that, by submitting this pull request, I am responsible for all of the changes contained within these commits.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4618
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 18:27:36 +01:00
crueter db83070bf2 [meta] Add FUNDING.yml (#4613)
Forgejo 17.0.0 adds support for FUNDING.yml, this also shows up on
GitHub.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
- [x] I have read and followed the [website policy](https://git.eden-emu.dev/eden-emu/site-policy).
- [x] I affirm that the content of this pull request body was written and submitted entirely by me, in my own words, and not by an AI agent or LLM.
- [ ] I affirm that I did not read any of this, and understand that my pull request will be closed if I check this box.
- [x] I affirm that, by submitting this pull request, I am responsible for all of the changes contained within these commits.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4613
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-11-01 05:16:02 +01:00
crueter e903d70d9e [docs] Add site policy, disallow AI-written PRs/issues and spam (#4612)
The spam got a little out of hand.

Site policy also disallows things such as asking for donations without
giving back to us, piracy, large file uploads, illegal stuff, and more.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
- [x] I have read and followed the [website policy](https://git.eden-emu.dev/eden-emu/site-policy).
- [x] I affirm that the content of this pull request body was written and submitted entirely by me, in my own words, and not by an AI agent or LLM.
- [ ] I affirm that I did not read any of this, and understand that my pull request will be closed if I check this box.
- [x] I affirm that, by submitting this pull request, I am responsible for all of the changes contained within these commits.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4612
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: John <john@eden-emu.dev>
2026-11-01 03:19:27 +01:00
lizzie 802531c457 [ports/*] Fix cross-compilation issues (#4493)
OpenSSL needs a patch to properly cross compile under rv64; fix infinite recursion with A32 thunks.
Updated cross build guide as well.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4493
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 03:12:55 +01:00
lizzie bd071e3464 [dynarmic] Add A64:SM4/SM3 tests (#4513)
Trivial change for upcoming work.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4513
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 03:12:03 +01:00
lizzie a845100bf0 [cmake] Cleanup dynarmic's CMake (#4365)
Removes a handful of unused/untested features (like disabling CPU detection)
and brings the CMake of dynarmic more in line with the rest of the build system.

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4365
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-11-01 03:11:26 +01:00
84 changed files with 1192 additions and 3253 deletions
+2
View File
@@ -0,0 +1,2 @@
liberapay: crueter
custom: 'https://www.paypal.biz/edenemulator'
+4
View File
@@ -1,4 +1,8 @@
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. - [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
- [ ] I have read and followed the [website policy](https://git.eden-emu.dev/eden-emu/site-policy).
- [ ] I affirm that the content of this pull request body was written and submitted entirely by me, in my own words, and not by an AI agent or LLM.
- [ ] I affirm that I did not read any of this, and understand that my pull request will be closed if I check this box.
- [ ] I affirm that, by submitting this pull request, I am responsible for all of the changes contained within these commits.
------------------- -------------------
@@ -0,0 +1,40 @@
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 3012e05..2ae23ff 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -108,7 +108,8 @@ function(configure_openssl)
)
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
message(STATUS "Found previous configure results. Don't perform configuration")
return()
endif()
@@ -134,10 +135,24 @@ function(configure_openssl)
set(VERBOSE_OPTION OUTPUT_QUIET)
endif()
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
+ find_program(EMCC emcc REQUIRED)
+ set(EMSCRIPTEN_LINKER ${EMCC})
+ list(APPEND CONFIGURE_COMMAND wasm64)
+ else()
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
+ endif ()
+
execute_process(
- COMMAND ${CMAKE_COMMAND} -E env
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
"CFLAGS=${CMAKE_C_FLAGS}"
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
+ "CC=${CMAKE_C_COMPILER}"
+ "CXX=${CMAKE_CXX_COMPILER}"
+ "LD=${EMSCRIPTEN_LINKER}"
${CONFIGURE_COMMAND}
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
${VERBOSE_OPTION}
+5 -2
View File
@@ -1,7 +1,10 @@
# AGENTS # AGENTS
Read and follow the AI policy found in `docs/policies/AI.md`, and the coding policies found in `docs/policies/Coding.md`. Read and follow the [contribution guidelines](./CONTRIBUTING.md) and our [site policy](https://git.eden-emu.dev/eden-emu/site-policy/src/branch/master)..
Do not, under any circumstances, ever submit PRs or push commits on behalf of the user. Do not, under any circumstances, ever:
- Submit PRs, push commits, or request any API calls on behalf of the user.
- Write commit messages, PR descriptions, or issues on behalf of the user.
Keep comments to a reasonable level. Do not overexplain or add/remove comments from unrelated parts of the codebase. Keep comments to a reasonable level. Do not overexplain or add/remove comments from unrelated parts of the codebase.
+26 -2
View File
@@ -16,8 +16,11 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
### Policies ### Policies
- AI usage is allowed, but we reserve the right to close and rework your PR if you don't fully understand the changes you're making. - AI usage is allowed, but we reserve the right to close and rework your PR if you don't fully understand the changes you're making. Additionally:
- As a common courtesy, please disclose your AI usage under the PR description, as `Assisted-by: <AI model> (<how it helped>)`. - As a common courtesy, please disclose your AI usage under the PR description, as `Assisted-by: <AI model> (<how it helped>)`.
- All PR descriptions, issue bodies, and comments **must** be written by you, in your own words.
- AI-written comments/descriptions/bodies are not only difficult for us to read, they also indicate to us that you did not put in the effort to actually understand your changes. These will be closed, deleted, or hidden without warning.
- Spamming of low-effort AI-generated pull requests or issues is considered a [Human Effort Denial of Service](https://tiangolo.com/open-source/contributing/#human-effort-denial-of-service), and will result in the immediate suspension of your Forgejo account, or you getting blocked from the organization on other platforms.
- Usage of any form of profanity or otherwise unsavory language is discouraged but not strictly disallowed. - Usage of any form of profanity or otherwise unsavory language is discouraged but not strictly disallowed.
- New code must follow the same style as the surrounding codebase. Exceptions may be granted in certain cases. - New code must follow the same style as the surrounding codebase. Exceptions may be granted in certain cases.
- Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this. - Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this.
@@ -26,6 +29,25 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
- While we do *not* adhere to the terms of a formal code of conduct, you are expected to treat other developers, contributors, and community members respectfully. - While we do *not* adhere to the terms of a formal code of conduct, you are expected to treat other developers, contributors, and community members respectfully.
- You **must** have basic knowledge of [Git](https://git-scm.com/learn). Knowing how to manage your branches and follow proper fork policies is a necessity. - You **must** have basic knowledge of [Git](https://git-scm.com/learn). Knowing how to manage your branches and follow proper fork policies is a necessity.
### Pull Requests
Pull requests are only to be merged by core developers when properly tested and discussions conclude on Discord or other communication channels. Labels are recommended but not required. However, all PRs MUST be namespaced and optionally typed:
```txt
[cmake] refactor: CPM over submodules
[desktop] feat: implement firmware install from ZIP
[hle] stub fw20 functions
[core] test: raise maximum CPU cores to 6
[cmake, core] Unbreak FreeBSD Building Process
```
- The level of namespacing is generally left to the committer's choice.
- However, we never recommend going more than two levels *except* in `hle`, in which case you may go as many as four levels depending on the specificity of your changes.
- Ocassionally, up to two additional namespaces may be provided for more clarity.
- Changes that affect the entire project (sans CMake changes) should be namespaced as `meta`.
- Maintainers are permitted to change namespaces at will.
- Commits within PRs are not required to be namespaced, but it is highly recommended.
### Where do I start? ### Where do I start?
Anywhere you like! If you are facing an issue and want to fix it, go ahead. For new features, you are heavily encouraged to open a feature request on GitHub, Codeberg, or Forgejo first, discussing the motivations, potential implementation, and user flow of your desired feature. Our UI/UX designers will work with you to refine your feature before you actually choose to implement it. Anywhere you like! If you are facing an issue and want to fix it, go ahead. For new features, you are heavily encouraged to open a feature request on GitHub, Codeberg, or Forgejo first, discussing the motivations, potential implementation, and user flow of your desired feature. Our UI/UX designers will work with you to refine your feature before you actually choose to implement it.
@@ -39,6 +61,8 @@ There are five primary ways to contribute.
- Submit pull requests directly to our source tree - Submit pull requests directly to our source tree
- You must first [create an account](https://git.eden-emu.dev/user/sign_up). - You must first [create an account](https://git.eden-emu.dev/user/sign_up).
- You may use an internal account, or you may use a GitHub/Codeberg account linked to our instance. - You may use an internal account, or you may use a GitHub/Codeberg account linked to our instance.
- Please make sure to read our [site policy](https://git.eden-emu.dev/eden-emu/site-policy/src/branch/master).
- Both your account and your associated fork must be public, *not* limited/private, otherwise our CI workflow will not work on your pull requests.
- Submit pull requests to our [GitHub mirror](https://github.com/eden-emulator/Issue-Reports) - Submit pull requests to our [GitHub mirror](https://github.com/eden-emulator/Issue-Reports)
- Note that this is subject to being removed by DMCA, so don't rely on this. - Note that this is subject to being removed by DMCA, so don't rely on this.
- Submit pull requests to our [Codeberg mirror](https://codeberg.org/eden-emu/eden) - Submit pull requests to our [Codeberg mirror](https://codeberg.org/eden-emu/eden)
@@ -48,7 +72,7 @@ There are five primary ways to contribute.
- Alongside the contents of your email, please attach the patch/diff file itself for easy access and use. - Alongside the contents of your email, please attach the patch/diff file itself for easy access and use.
- Email our developers at [`developers@eden-emu.dev`](mailto:developers@eden-emu.dev) with any of your relevant findings or code changes. - Email our developers at [`developers@eden-emu.dev`](mailto:developers@eden-emu.dev) with any of your relevant findings or code changes.
To test your changes, ensure to read the [build documentation](./docs/Build.md) for your specific platform(s) to ensure everything compiles and works properly. You should also make sure that your branch is up-to-date with the upstream `master` branch before opening a pull request. To test your changes, ensure to read the [build documentation](./docs/Build.md) for your specific platform(s) to ensure everything compiles and works properly. You should also make sure that your branch is up-to-date with the upstream `master` branch before opening a pull request. **You** are responsible for ensuring that your contribution does what it says and doesn't break everything else horribly.
## Non-code Contributions ## Non-code Contributions
+2 -1
View File
@@ -202,7 +202,8 @@
"0001-cpmutil-compat.patch", "0001-cpmutil-compat.patch",
"0002-use-ccache.patch", "0002-use-ccache.patch",
"0003-use-cmake-compiler-flags.patch", "0003-use-cmake-compiler-flags.patch",
"0004-use-shell-wrapper.patch" "0004-use-shell-wrapper.patch",
"0005-cross-compile.patch"
], ],
"repo": "jimmy-park/openssl-cmake", "repo": "jimmy-park/openssl-cmake",
"version": "3.6.2" "version": "3.6.2"
+1 -230
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

+1 -289
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

+1 -144
View File
@@ -1,144 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" fill="none" viewBox="0 0 512 512"><defs><linearGradient id="c"><stop offset="0" style="stop-color:#f71baa;stop-opacity:.56623769"/><stop offset="1" style="stop-color:#fa87d4;stop-opacity:.25"/></linearGradient><linearGradient id="b"><stop offset="0" style="stop-color:#fe00b1;stop-opacity:1"/><stop offset="1" style="stop-color:#fe00b1;stop-opacity:0"/></linearGradient><linearGradient id="a"><stop offset="0" style="stop-color:#ff0950;stop-opacity:.5"/><stop offset=".496" style="stop-color:#bf42f6;stop-opacity:.75"/><stop offset="1" style="stop-color:#5da5ed;stop-opacity:1"/></linearGradient><linearGradient xlink:href="#a" id="d" x1="270.4" x2="270.4" y1="40" y2="494.4" gradientTransform="translate(-33.928 -34.199)scale(1.07505)" gradientUnits="userSpaceOnUse" spreadMethod="reflect"/><linearGradient xlink:href="#b" id="f" x1="125.402" x2="431.024" y1="271.834" y2="271.834" gradientTransform="translate(-33.928 -34.199)scale(1.07505)" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#c" id="e" x1="270.4" x2="270.4" y1="33.584" y2="500.816" gradientTransform="translate(-33.928 -34.199)scale(1.07505)" gradientUnits="userSpaceOnUse"/></defs><circle cx="256.764" cy="253.054" r="244.25" style="fill:url(#d);fill-opacity:1;stroke:url(#e);stroke-width:13.7948;stroke-opacity:.566238;paint-order:stroke fill markers"/><path d="M55.701 114.372a244 244 0 0 0-10.836 17.2h423.797a244 244 0 0 0-10.837-17.2z" style="fill:#fff;fill-opacity:.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="m219.665 499.138-10.338-55.949" style="fill:#ff2bd5;fill-opacity:1;stroke-width:14.3776;stroke-opacity:.415999;paint-order:stroke fill markers"/><path d="M58.272 395.39a244 244 0 0 0 13.54 17.2h369.902a244 244 0 0 0 13.541-17.2zM24.605 328.953a244 244 0 0 0 6.347 17.2h451.623a244 244 0 0 0 6.347-17.2zM12.586 259.075a244 244 0 0 0 1.033 17.2h486.289a244 244 0 0 0 1.033-17.2z" style="fill:#fff;fill-opacity:.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M306.217 12.242s-11.75 42.031-17.703 63.363c-12.713 9.965-21.075 19.29-21.075 19.29s-22.956-32.534-44.85-46.065c-21.892-13.531-51.398-11.788-51.398-11.788s-12.257 0-27.092 5.806c-14.836 5.805-20.857 9.675-20.857 9.675s15.053-.43 37.199 7.095 31.82 12.042 31.82 12.042l30.423-6.881-8.277 11.611s14.514 9.353 22.362 17.739c7.848 8.385 11.502 13.114 11.502 13.114s-13.652-5.697-55.579-1.826-83.209 59.127-83.209 59.127 40.42-16.771 64.287-22.362c23.866-5.59 46.657-8.386 46.657-8.386s-18.06 7.74-37.41 40.207c-19.351 32.467-13.762 84.07-13.762 84.07s30.747-61.923 60.203-82.778 35.476-21.501 35.476-21.501-23.65 50.742-32.896 132.23c-9.245 81.489 29.671 217.804 29.671 217.804l46.872-7.311s-44.721-44.291-48.591-158.676 16.34-182.758 16.34-182.758 16.771 1.29 41.712 34.401 39.56 60.203 39.56 60.203 9.463-53.753-11.178-78.264-52.463-35.26-52.463-35.26 13.438-6.988 41.282-4.945c27.844 2.042 85.6 28.444 85.6 28.444S405.336 132.903 369 114.355s-79.77.307-79.77.307 4.055-16.119 28.533-34.21C342.24 62.358 373.56 44.42 373.56 44.42s-25.845 1.217-54.124 12.62c-7.036 2.837-13.79 6.605-19.966 10.666l17.067-53.528z" style="fill:url(#f);fill-opacity:1;stroke:#fff;stroke-width:17.2007;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"/></svg>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="base.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7">
<linearGradient
id="linearGradient24"
inkscape:collect="always">
<stop
style="stop-color:#f71baa;stop-opacity:0.56623769;"
offset="0"
id="stop24" />
<stop
style="stop-color:#fa87d4;stop-opacity:0.25;"
offset="1"
id="stop25" />
</linearGradient>
<rect
x="22.627417"
y="402.76802"
width="521.34025"
height="248.94868"
id="rect24" />
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#fe00b1;stop-opacity:1;"
offset="0"
id="stop1" />
<stop
style="stop-color:#fe00b1;stop-opacity:0;"
offset="1"
id="stop2" />
</linearGradient>
<linearGradient
id="linearGradient11"
inkscape:collect="always">
<stop
style="stop-color:#ff0950;stop-opacity:0.5;"
offset="0"
id="stop11" />
<stop
style="stop-color:#bf42f6;stop-opacity:0.75;"
offset="0.49556771"
id="stop20" />
<stop
style="stop-color:#5da5ed;stop-opacity:1;"
offset="1"
id="stop12" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient11"
id="linearGradient12"
x1="270.39996"
y1="39.999989"
x2="270.39996"
y2="494.39996"
gradientUnits="userSpaceOnUse"
spreadMethod="reflect"
gradientTransform="matrix(1.075046,0,0,1.075046,-33.928383,-34.198735)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient2"
x1="125.40197"
y1="271.834"
x2="431.02424"
y2="271.834"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.075046,0,0,1.075046,-33.928383,-34.198735)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient24"
id="linearGradient25"
x1="270.39996"
y1="33.58408"
x2="270.39996"
y2="500.81589"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.075046,0,0,1.075046,-33.928383,-34.198735)" />
</defs>
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.88388348"
inkscape:cx="141.98704"
inkscape:cy="265.87215"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7" />
<circle
style="fill:url(#linearGradient12);fill-opacity:1;stroke:url(#linearGradient25);stroke-width:13.7948;stroke-opacity:0.566238;paint-order:stroke fill markers"
id="path8"
cx="256.76401"
cy="253.05354"
r="244.25046" />
<path
id="rect1-3"
style="fill:#ffffff;fill-opacity:0.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 55.701477,114.37179 A 244.25044,244.25044 0 0 0 44.86493,131.57252 h 423.79699 a 244.25044,244.25044 0 0 0 -10.83655,-17.20073 z" />
<path
style="fill:#ff2bd5;fill-opacity:1;stroke-width:14.3776;stroke-opacity:0.415999;paint-order:stroke fill markers"
d="M 219.6651,499.13816 209.32675,443.18948"
id="path9" />
<path
id="rect1-2"
style="fill:#ffffff;fill-opacity:0.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 58.271509,395.38922 a 244.25044,244.25044 0 0 0 13.54096,17.20073 H 441.71437 a 244.25044,244.25044 0 0 0 13.54097,-17.20073 z" />
<path
id="rect1-2-5"
style="fill:#ffffff;fill-opacity:0.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 24.604933,328.95264 a 244.25044,244.25044 0 0 0 6.347389,17.20074 H 482.57453 a 244.25044,244.25044 0 0 0 6.34738,-17.20074 z" />
<path
id="rect1"
style="fill:#ffffff;fill-opacity:0.5;stroke:none;stroke-width:17.2007;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 12.586253,259.07464 a 244.25044,244.25044 0 0 0 1.033053,17.20075 H 499.90754 a 244.25044,244.25044 0 0 0 1.03305,-17.20075 z" />
<path
id="path15"
style="fill:url(#linearGradient2);fill-opacity:1;stroke:#ffffff;stroke-width:17.2007;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
d="m 306.21658,12.242412 c 0,0 -11.75029,42.031067 -17.70256,63.362621 -12.7133,9.964494 -21.07468,19.289937 -21.07468,19.289937 0,0 -22.95662,-32.534232 -44.84956,-46.065299 C 200.69681,35.298604 171.19123,37.04196 171.19123,37.04196 c 0,0 -12.25678,4.2e-4 -27.09242,5.805668 -14.83563,5.805249 -20.85631,9.675414 -20.85631,9.675414 0,0 15.05232,-0.430438 37.19828,7.094883 22.14594,7.525322 31.82093,12.041775 31.82093,12.041775 l 30.42255,-6.880714 -8.27702,11.611336 c 0,0 14.51396,9.3529 22.36179,17.738259 7.84785,8.385359 11.50216,13.114729 11.50216,13.114729 0,0 -13.65224,-5.69691 -55.57904,-1.82674 -41.92679,3.87016 -83.20898,59.12752 -83.20898,59.12752 0,0 40.42048,-16.77155 64.28649,-22.36179 23.86602,-5.59024 46.65742,-8.3862 46.65742,-8.3862 0,0 -18.05952,7.74075 -37.41035,40.20714 -19.35082,32.46639 -13.76142,84.06986 -13.76142,84.06986 0,0 30.74631,-61.92266 60.20258,-82.77854 29.45625,-20.8559 35.47652,-21.50092 35.47652,-21.50092 0,0 -23.6506,50.74216 -32.896,132.23065 -9.24539,81.48849 29.67085,217.8039 29.67085,217.8039 l 46.87159,-7.31116 c 0,0 -44.72107,-44.29147 -48.59124,-158.67636 -3.87017,-114.3849 16.33986,-182.75782 16.33986,-182.75782 0,0 16.77156,1.29005 41.71263,34.40147 24.94106,33.11142 39.56043,60.20258 39.56043,60.20258 0,0 9.46208,-53.75314 -11.1788,-78.26419 -20.64088,-24.51105 -52.46309,-35.26025 -52.46309,-35.26025 0,0 13.4385,-6.98738 41.28219,-4.94479 27.84369,2.04258 85.60053,28.44463 85.60053,28.44462 0,0 -15.50743,-26.7591 -51.84367,-45.30731 -36.33623,-18.548198 -79.76967,0.30655 -79.76967,0.30655 0,0 4.05525,-16.118294 28.53281,-34.210394 24.47756,-18.092095 55.79742,-36.030833 55.79742,-36.030833 0,0 -25.84554,1.216607 -54.12395,12.619193 -7.03608,2.837127 -13.78978,6.605056 -19.96604,10.666471 l 17.06635,-53.527632 z" />
</svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+1 -230
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+1 -672
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+1 -80
View File
@@ -1,80 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" fill="none" viewBox="0 0 512 512"><defs><linearGradient id="a"><stop offset="0" style="stop-color:#f977d9;stop-opacity:1"/><stop offset=".541" style="stop-color:#a655d5;stop-opacity:1"/><stop offset=".571" style="stop-color:#984fd5;stop-opacity:1"/><stop offset=".597" style="stop-color:#8c4ad4;stop-opacity:1"/><stop offset="1" style="stop-color:#3928d2;stop-opacity:1"/></linearGradient><linearGradient xlink:href="#a" id="b" x1="264.175" x2="264.175" y1="28.386" y2="488.651" gradientTransform="matrix(.97816 0 0 .9788 4.57 5.88)" gradientUnits="userSpaceOnUse"/></defs><path d="M262.975 33.688A218.443 225.23 0 0 0 138.838 73.59l2.3-1.324 20.083-7.07 19.799-3.678 23.334 2.687L228.96 76.65 265.8 115 283 99.4l21.6-13.6 26.4-9.4 29.6-6.8 19.355-.391a218.443 225.23 0 0 0-116.98-35.521M380.076 69.29l-29.877 8.51-21 11.398L307.4 108l-18.6 21.8-1.4 5.2 15-5.4 16.6-1.8 29.8 4 21.4 9.6 22.8 20 6.6 10.2-24.8-10.8-36.4-10-29.2-.8-20.4 4.4 4.4.8 26 13.6 18.8 18.4 12.8 36.8-1.6 36-23.2-37.2-23.2-28.8-25.2-21.6-8.4 15.2-12.4 94L258 364l20.4 88.8 18.795 28.57a218.443 225.23 0 0 0 184.223-222.452A218.443 225.23 0 0 0 380.076 69.291M133.074 77.84a218.443 225.23 0 0 0-88.54 181.078A218.443 225.23 0 0 0 257.04 484.066l-9.695-25.437-9.475-40.445-4.809-37.76-2.687-25.457-.424-33.658 2.404-29.98 5.092-36.347 17.112-63.074 7.07-19.375-6.08 1.557-14.567 6.787-11.382 9.322-19 18-13.2 21.201-11 23.2-13 16.4-2-15.8 2.2-23.8 9.4-27 16.4-24.4 21.641-24.559-12.02 2.264-31.962 7.07-28.99 8.627L119.925 183l.707-2.83 9.191-14.283 21.356-18.385 21.779-12.02 17.111-5.941 18.951-1.555 18.667.707 20.082-.283-1.272-3.252-19.799-19.234-7.072-8.485 1.84-7.353-14.71 3.959-10.888 1.414-16.406-6.787-23.334-7.213z" style="fill:url(#b);fill-opacity:1;stroke:none;stroke-width:13.3314;stroke-opacity:1;paint-order:stroke fill markers"/><path d="M259.367 490.166 298.4 483.2l-20-30.4L258 364l-1.2-82.4 12.4-94 8.4-15.2 25.2 21.6 23.2 28.8 23.2 37.2 1.6-36-12.8-36.8-18.8-18.4-26-13.6-4.4-.8 20.4-4.4 29.2.8 36.4 10 24.8 10.8-6.6-10.2-22.8-20-21.4-9.6-29.8-4-16.6 1.8-15 5.4 1.4-5.2 18.6-21.8 21.8-18.8 21-11.4 30.2-8.6-19.8.4-29.6 6.8-26.4 9.4L283 99.4 265.8 115 228.96 76.65l-24.607-12.445-23.335-2.687-19.799 3.677-20.082 7.071-9.333 5.374 24.324 3.818 23.335 7.213 16.404 6.788 10.89-1.414 14.708-3.96-1.839 7.354 7.071 8.485 19.8 19.234 1.272 3.252-20.082.283-18.667-.707-18.95 1.556-17.113 5.94-21.779 12.02-21.354 18.385-9.193 14.284-.707 2.828 28.143-11.597 28.991-8.626 31.962-7.071 12.02-2.263L199.4 178 183 202.4l-9.4 27-2.2 23.8 2 15.8 13-16.4 11-23.2 13.2-21.2 19-18 11.382-9.322 14.566-6.789 6.081-1.555-7.07 19.375-17.113 63.073-5.09 36.346-2.405 29.981.424 33.658 2.687 25.456 4.809 37.76 9.475 40.446z" style="fill:#1b1b1b;fill-opacity:.12492698;stroke:none;stroke-width:13.374;stroke-opacity:.415999;paint-order:stroke fill markers"/></svg>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="base_small.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7">
<linearGradient
id="linearGradient10"
inkscape:collect="always">
<stop
style="stop-color:#f977d9;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#a655d5;stop-opacity:1;"
offset="0.54051435"
id="stop13" />
<stop
style="stop-color:#984fd5;stop-opacity:1;"
offset="0.5714342"
id="stop12" />
<stop
style="stop-color:#8c4ad4;stop-opacity:1;"
offset="0.59666741"
id="stop14" />
<stop
style="stop-color:#3928d2;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient10"
id="linearGradient11"
x1="264.17508"
y1="28.385544"
x2="264.17508"
y2="488.65109"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.97815818,0,0,0.97880258,4.570042,5.8799159)" />
</defs>
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.88388348"
inkscape:cx="153.30075"
inkscape:cy="243.24473"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7"
showguides="false" />
<path
id="path9"
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:13.3314;stroke-opacity:1;paint-order:stroke fill markers"
d="M 262.97461 33.6875 A 218.44267 225.23091 0 0 0 138.83789 73.589844 L 141.13867 72.265625 L 161.2207 65.195312 L 181.01953 61.517578 L 204.35352 64.205078 L 228.96094 76.650391 L 265.80078 115 L 283 99.400391 L 304.59961 85.800781 L 331 76.400391 L 360.59961 69.599609 L 379.95508 69.208984 A 218.44267 225.23091 0 0 0 262.97461 33.6875 z M 380.07617 69.291016 L 350.19922 77.800781 L 329.19922 89.199219 L 307.40039 108 L 288.80078 129.80078 L 287.40039 135 L 302.40039 129.59961 L 319 127.80078 L 348.80078 131.80078 L 370.19922 141.40039 L 393 161.40039 L 399.59961 171.59961 L 374.80078 160.80078 L 338.40039 150.80078 L 309.19922 150 L 288.80078 154.40039 L 293.19922 155.19922 L 319.19922 168.80078 L 338 187.19922 L 350.80078 224 L 349.19922 260 L 326 222.80078 L 302.80078 194 L 277.59961 172.40039 L 269.19922 187.59961 L 256.80078 281.59961 L 258 364 L 278.40039 452.80078 L 297.19531 481.36914 A 218.44267 225.23091 0 0 0 481.41797 258.91797 A 218.44267 225.23091 0 0 0 380.07617 69.291016 z M 133.07422 77.839844 A 218.44267 225.23091 0 0 0 44.533203 258.91797 A 218.44267 225.23091 0 0 0 257.04102 484.06641 L 247.3457 458.62891 L 237.87109 418.18359 L 233.0625 380.42383 L 230.375 354.9668 L 229.95117 321.30859 L 232.35547 291.32812 L 237.44727 254.98242 L 254.55859 191.9082 L 261.62891 172.5332 L 255.54883 174.08984 L 240.98242 180.87695 L 229.59961 190.19922 L 210.59961 208.19922 L 197.40039 229.40039 L 186.40039 252.59961 L 173.40039 269 L 171.40039 253.19922 L 173.59961 229.40039 L 183 202.40039 L 199.40039 178 L 221.04102 153.44141 L 209.02148 155.70508 L 177.05859 162.77539 L 148.06836 171.40234 L 119.92578 183 L 120.63281 180.16992 L 129.82422 165.88672 L 151.17969 147.50195 L 172.95898 135.48242 L 190.07031 129.54102 L 209.02148 127.98633 L 227.6875 128.69336 L 247.76953 128.41016 L 246.49805 125.1582 L 226.69922 105.92383 L 219.62695 97.439453 L 221.4668 90.085938 L 206.75781 94.044922 L 195.86914 95.458984 L 179.46289 88.671875 L 156.12891 81.458984 L 133.07422 77.839844 z " />
<path
style="fill:#1b1b1b;fill-opacity:0.12492698;stroke:none;stroke-width:13.374;stroke-opacity:0.415999;paint-order:stroke fill markers"
d="m 259.36665,490.16617 39.03323,-6.96642 -20,-30.4 -20.4,-88.8 -1.2,-82.4 12.4,-94 8.4,-15.2 25.2,21.6 23.2,28.8 23.2,37.2 1.6,-36 -12.8,-36.8 -18.8,-18.4 -26,-13.6 -4.4,-0.8 20.4,-4.4 29.2,0.8 36.4,10 24.8,10.8 -6.6,-10.2 -22.8,-20 -21.4,-9.6 -29.8,-4 -16.6,1.8 -15,5.4 1.4,-5.2 18.6,-21.8 21.8,-18.800003 21,-11.4 30.2,-8.6 -19.8,0.4 -29.6,6.8 -26.4,9.4 -21.6,13.6 -17.2,15.600003 -36.83882,-38.349628 -24.60732,-12.445079 -23.33452,-2.687006 -19.79899,3.676955 -20.08184,7.071068 -9.33381,5.374012 24.32448,3.818376 23.33452,7.212489 16.40488,6.788226 10.88944,-1.414214 14.70782,-3.959798 -1.83847,7.353911 7.07106,8.485288 19.79899,19.2333 1.2728,3.25269 -20.08184,0.28284 -18.66762,-0.7071 -18.95046,1.55563 -17.11198,5.9397 -21.77889,12.02081 -21.35462,18.38478 -9.19239,14.28356 -0.70711,2.82843 28.14285,-11.59656 28.99138,-8.6267 31.96122,-7.07107 12.02082,-2.26274 -21.64158,24.55783 -16.4,24.4 -9.4,27 -2.2,23.8 2,15.8 13,-16.4 11,-23.2 13.2,-21.2 19,-18 11.38199,-9.32209 14.5664,-6.78822 6.08112,-1.55564 -7.07107,19.37473 -17.11198,63.07393 -5.09117,36.34528 -2.40416,29.98133 0.42426,33.65828 2.68701,25.45585 4.80832,37.7595 9.47523,40.44651 z"
id="path8-4" />
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

+25 -4
View File
@@ -6,10 +6,31 @@ General guide for cross compiling.
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main. A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
- Install QEMU: `sudo pkg install qemu` ```sh
- Download Debian 13: `wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso` #!/bin/sh
- Create a system disk: `qemu-img create -f qcow2 debian-13-arm64-ci.qcow2 30G` ISO_FILE="debian-13.0.0-arm64-netinst.iso"
- Run the VM: `qemu-system-aarch64 -M virt -m 2G -cpu max -bios /usr/local/share/qemu/edk2-aarch64-code.fd -drive if=none,file=debian-13.0.0-arm64-netinst.iso,format=raw,id=cdrom -device scsi-cd,drive=cdrom -drive if=none,file=debian-13-arm64-ci.qcow2,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 -device virtio-gpu-pci -device usb-ehci -device usb-kbd -device intel-hda -device hda-output -nic user,model=virtio-net-pci` DISK_FILE="debian-13-arm64-ci.qcow2"
which -- qemu-system-aarch64 || sudo pkg install qemu
[ -f "$ISO_FILE" ] || wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/$ISO_FILE
[ -f "$DISK_FILE" ] || qemu-img create -f qcow2 "$DISK_FILE" 30G
QEMU_FLAGS=(
-M virt
-m 2G
-cpu max
-bios /usr/local/share/qemu/edk2-aarch64-code.fd
-drive if=none,file=$ISO_FILE,format=raw,id=cdrom
-drive if=none,file=$DISK_FILE,id=hd0,format=qcow2
-device scsi-cd,drive=cdrom
-device virtio-blk-device,drive=hd0
-device virtio-gpu-pci
-device usb-ehci
-device usb-kbd
-device intel-hda
-device hda-output
-nic user,model=virtio-net-pci
)
qemu-system-aarch64 "${QEMU_FLAGS[@]}"
```
## Gentoo ## Gentoo
-19
View File
@@ -24,25 +24,6 @@ If the work is licensed/vendored from other people or projects, you may omit the
For more information on the license header script, run `.ci/license-header.sh -h`. For more information on the license header script, run `.ci/license-header.sh -h`.
## Pull Requests
Pull requests are only to be merged by core developers when properly tested and discussions conclude on Discord or other communication channels. Labels are recommended but not required. However, all PRs MUST be namespaced and optionally typed:
```txt
[cmake] refactor: CPM over submodules
[desktop] feat: implement firmware install from ZIP
[hle] stub fw20 functions
[core] test: raise maximum CPU cores to 6
[cmake, core] Unbreak FreeBSD Building Process
```
- The level of namespacing is generally left to the committer's choice.
- However, we never recommend going more than two levels *except* in `hle`, in which case you may go as many as four levels depending on the specificity of your changes.
- Ocassionally, up to two additional namespaces may be provided for more clarity.
- Changes that affect the entire project (sans CMake changes) should be namespaced as `meta`.
- Maintainers are permitted to change namespaces at will.
- Commits within PRs are not required to be namespaced, but it is highly recommended.
## Adding new settings ## Adding new settings
When adding new settings, use `tr("Setting:")` if the setting is meant to be a field, otherwise use `tr("Setting")` if the setting is meant to be a Yes/No or checkmark type of setting, see [this short style guide](https://learn.microsoft.com/en-us/style-guide/punctuation/colons#in-ui). When adding new settings, use `tr("Setting:")` if the setting is meant to be a field, otherwise use `tr("Setting")` if the setting is meant to be a Yes/No or checkmark type of setting, see [this short style guide](https://learn.microsoft.com/en-us/style-guide/punctuation/colons#in-ui).
+1 -1
View File
@@ -28,7 +28,7 @@ This contains documentation created by developers, build instructions, guideline
Policies and information on development. Policies and information on development.
- **[AI and LLM Usage](./policies/AI.md)** - **[AI and LLM Usage](./CONTRIBUTING.md#policies)**
- **[Coding guidelines](./policies/Coding.md)** - **[Coding guidelines](./policies/Coding.md)**
- **[Contributing](../CONTRIBUTING.md)** - **[Contributing](../CONTRIBUTING.md)**
+3 -4
View File
@@ -33,10 +33,9 @@ Eden by default will be cloned into:
## Building with Terminal ## Building with Terminal
1. Download the SDK and NDK from Android Studio. 1. Download the SDK and NDK from Android Studio.
2. Navigate to SDK and NDK paths. 2. Navigate to SDK path.
3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via 3. Then set ANDROID_HOME in terminal via
`export ANDROID_SDK_ROOT=path/to/sdk` `export ANDROID_HOME=path/to/sdk`
`export ANDROID_NDK_ROOT=path/to/ndk`.
4. Navigate to `eden/src/android`. 4. Navigate to `eden/src/android`.
5. Then Build with `./gradlew assembleRelWithDebInfo`. 5. Then Build with `./gradlew assembleRelWithDebInfo`.
6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`. 6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`.
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 120 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+5 -5
View File
@@ -101,6 +101,11 @@ endfunction()
# ia64 exists # ia64 exists
# the rest exist, but are probably less popular than ia64 # the rest exist, but are probably less popular than ia64
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
detect_architecture_symbols( detect_architecture_symbols(
ARCH arm64 ARCH arm64
SYMBOLS SYMBOLS
@@ -203,11 +208,6 @@ detect_architecture_symbols(
"__loongarch__" "__loongarch__"
"__loongarch64") "__loongarch64")
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
# "generic" target # "generic" target
# If you have reached this point, you're on some as-of-yet unsupported architecture. # If you have reached this point, you're on some as-of-yet unsupported architecture.
# See the docs up above for known unsupported architectures # See the docs up above for known unsupported architectures
@@ -79,9 +79,8 @@ class DriverFetcherFragment : Fragment() {
IntRange(100, 599) to "Unsupported", IntRange(100, 599) to "Unsupported",
IntRange(600, 639) to "Mr. Purple EOL-24.3.4", IntRange(600, 639) to "Mr. Purple EOL-24.3.4",
IntRange(640, 699) to "Mr. Purple T19", IntRange(640, 699) to "Mr. Purple T19",
IntRange(700, 710) to "KIMCHI 25.2.0_r5", IntRange(700, 710) to "Mr. Purple T29",
IntRange(711, 799) to "Mr. Purple T23", IntRange(711, 899) to "Mr. Purple T30",
IntRange(800, 899) to "GameHub Adreno 8xx",
IntRange(900, Int.MAX_VALUE) to "Unsupported" IntRange(900, Int.MAX_VALUE) to "Unsupported"
) )
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

@@ -498,7 +498,7 @@
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string> <string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
<string name="discord_link" translatable="false">https://discord.gg/HstXbPch7X</string> <string name="discord_link" translatable="false">https://discord.gg/HstXbPch7X</string>
<string name="stoat_link" translatable="false">https://stt.gg/qKgFEAbH</string> <string name="stoat_link" translatable="false">https://stt.gg/qKgFEAbH</string>
<string name="x_link" translatable="false">https://nitter.poast.org/edenemuofficial</string> <string name="x_link" translatable="false">https://x.com/edenemuofficial</string>
<string name="website_link" translatable="false">https://eden-emu.dev</string> <string name="website_link" translatable="false">https://eden-emu.dev</string>
<string name="github_link" translatable="false">https://git.eden-emu.dev/eden-emu</string> <string name="github_link" translatable="false">https://git.eden-emu.dev/eden-emu</string>
+202 -80
View File
@@ -7,18 +7,25 @@
#include <vector> #include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h"
#include "common/fs/file.h" #include "common/fs/file.h"
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#ifdef __ANDROID__ #ifdef __ANDROID__
#include "common/fs/fs_android.h" #include "common/fs/fs_android.h"
#endif #endif
#include "common/logging.h" #include "common/logging.h"
#include "common/literals.h"
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#include <share.h> #include <share.h>
#include <windows.h>
#else #else
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -244,42 +251,162 @@ FileType IOFile::GetType() const {
return file_type; return file_type;
} }
#if defined(__unix__)
static int PlatformMapReadOnly(IOFile& io, const char* path) {
io.mmap_fd = open(path, O_RDONLY);
if (io.mmap_fd > 0) {
struct stat st;
fstat(io.mmap_fd, &st);
io.mmap_size = st.st_size;
int map_flags = MAP_PRIVATE;
#ifdef MAP_PREFAULT_READ
// Prefaults reads so the final resulting pagetable from this big stupid mmap()
// isn't comically lazily loaded, we just coalesce everything in-place for our
// lovely mmap flags; if we didn't prefault the reads the page table will be
// constructed in-place (i.e on a read-by-read basis) causing lovely soft-faults
// which would nuke any performance gains.
//
// This of course incurs a cost in the initial mmap(2) call, but that is fine.
map_flags |= MAP_PREFAULT_READ;
#endif
#ifdef MAP_NOSYNC
// This causes physical media to not be synched to our file/memory
// This means that if the read-only file is written to, we won't see changes
// or we may see changes which are just funnily scattered, in any case
// this presumes the files won't be changed during execution
//
// Do not ever use this on write files (if we ever support that); this will create
// a fun amount of fragmentation on the disk.
map_flags |= MAP_NOSYNC;
#endif
#if defined(NDEBUG) && defined(MAP_NOCORE)
map_flags |= MAP_NOCORE;
#endif
#ifdef MAP_HUGE_2MB
map_flags |= MAP_HUGE_2MB; //2mb pages
#elif defined(MAP_ALIGNED)
// File must be big enough that it's worth to super align. We can't just super-align every
// file otherwise we will run out of alignments for actually important files :)
// System doesn't guarantee a super alignment, but if it's available it will delete
// about 3 layers(?) of the TLB tree for each read/write.
// Again the cost of faults may make this negligible gains, but hey, we gotta work
// what we gotta work with.
map_flags |= MAP_ALIGNED(21); //2^21 = 2mb use 2MB pages
#endif
io.mmap_base = (u8*)mmap(nullptr, io.mmap_size, PROT_READ, map_flags, io.mmap_fd, 0);
if (io.mmap_base == MAP_FAILED) {
close(io.mmap_fd);
io.mmap_fd = -1;
} else {
using namespace Common::Literals;
// For small files it is acceptable to use a full readahead
// See https://github.com/torvalds/linux/blob/e80d033851b3bc94c3d254ac66660ddd0a49d72c/include/linux/pagemap.h#L1392
if (u64(st.st_size) >= 256_MiB) {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_RANDOM);
} else {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_SEQUENTIAL);
}
}
}
return io.mmap_fd;
}
static void PlatformUnmap(IOFile& io) {
if (io.mmap_fd != -1) {
munmap(io.mmap_base, io.mmap_size);
close(io.mmap_fd);
io.mmap_fd = -1;
}
}
#elif defined(__APPLE__)
// NO IMPLEMENTATION YET
#else
static int PlatformMapReadOnly(IOFile& io, const wchar_t* path) {
io.file_handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (HANDLE(io.file_handle) != INVALID_HANDLE_VALUE) {
io.mapping_handle = CreateFileMappingW(HANDLE(io.file_handle), nullptr, PAGE_READONLY, 0, 0, nullptr);
if (io.mapping_handle) {
io.mmap_base = (u8*)MapViewOfFile(HANDLE(io.mapping_handle), FILE_MAP_READ, 0, 0, 0);
if (io.mmap_base) {
_LARGE_INTEGER pvalue;
GetFileSizeEx(io.file_handle, &pvalue);
io.mmap_size = uint32_t(pvalue.QuadPart);
} else {
CloseHandle(io.mapping_handle);
CloseHandle(io.file_handle);
return -1;
}
} else {
CloseHandle(io.file_handle);
return -1;
}
}
return 0;
}
static void PlatformUnmap(IOFile& io) {
if(io.mapping_handle) {
if(io.mmap_base)
UnmapViewOfFile(HANDLE(io.mmap_base));
CloseHandle(HANDLE(io.mapping_handle));
}
if(io.file_handle != INVALID_HANDLE_VALUE)
CloseHandle(HANDLE(io.file_handle));
}
#endif
void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) { void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Close(); Close();
file_path = path; file_path = path;
file_access_mode = mode; file_access_mode = mode;
file_type = type; file_type = type;
errno = 0; errno = 0;
#ifdef _WIN32 #ifdef _WIN32
// TODO: this probably can use better logic but oh well I'm not a windowser
file_handle = nullptr;
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file"); //: {}", path.c_str());
}
}
if (file_handle == nullptr) {
if (flag != FileShareFlag::ShareNone) { if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag)); file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else { } else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
} }
}
#elif __ANDROID__ #elif __ANDROID__
if (Android::IsContentUri(path)) { if (Android::IsContentUri(path)) {
ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!"); ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read); if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
int const fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) { if (fd != -1) {
file = fdopen(fd, "r"); file = fdopen(fd, "r");
const auto error_num = errno; if (errno != 0 && file == nullptr)
if (error_num != 0 && file == nullptr) { LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), strerror(errno));
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
strerror(error_num));
}
} else { } else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str()); LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
} }
}
} else { } else {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
} }
#elif defined(__HAIKU__) || defined(__managarm__) || defined(__OPENORBIS__) || defined(__APPLE__)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#elif defined(__unix__)
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
}
}
if (mmap_fd == -1) {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); // mmap(2) failed or simply we can't use it
}
#else #else
// Some other fancy OS (ahem fucking Darwin/Mac OSX)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif #endif
if (!IsOpen()) { if (!IsOpen()) {
const auto ec = std::error_code{errno, std::generic_category()}; const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
@@ -288,25 +415,25 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
} }
void IOFile::Close() { void IOFile::Close() {
if (!IsOpen()) { #if defined(__APPLE__)
return; // NO IMPLEMENTATION YET
} #else
PlatformUnmap(*this);
#endif
if (file) {
errno = 0; errno = 0;
const auto close_result = std::fclose(file) == 0; const auto close_result = std::fclose(file) == 0;
if (!close_result) { if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()}; const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
} }
file = nullptr; file = nullptr;
} }
}
bool IOFile::IsOpen() const { bool IOFile::IsOpen() const {
return file != nullptr; return file != nullptr || IsMappedFile();
} }
std::string IOFile::ReadString(size_t length) const { std::string IOFile::ReadString(size_t length) const {
@@ -323,80 +450,65 @@ size_t IOFile::WriteString(std::span<const char> string) const {
} }
bool IOFile::Flush() const { bool IOFile::Flush() const {
if (!IsOpen()) { ASSERT(!IsMappedFile());
return false; if (file) {
}
errno = 0; errno = 0;
auto const flush_result = std::fflush(file) == 0;
#ifdef _WIN32
const auto flush_result = std::fflush(file) == 0;
#else
const auto flush_result = std::fflush(file) == 0;
#endif
if (!flush_result) { if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()}; const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
} }
return flush_result; return flush_result;
} }
bool IOFile::Commit() const {
if (!IsOpen()) {
return false; return false;
} }
bool IOFile::Commit() const {
ASSERT(!IsMappedFile());
if (file) {
errno = 0; errno = 0;
#ifdef _WIN32 #ifdef _WIN32
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
#else #else
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
#endif #endif
if (!commit_result) { if (!commit_result) {
const auto ec = std::error_code{errno, std::generic_category()}; const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
} }
return commit_result; return commit_result;
} }
bool IOFile::SetSize(u64 size) const {
if (!IsOpen()) {
return false; return false;
} }
bool IOFile::SetSize(u64 size) const {
ASSERT(!IsMappedFile());
if (file) {
errno = 0; errno = 0;
#ifdef _WIN32 #ifdef _WIN32
const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0; const auto set_size_result = _chsize_s(fileno(file), s64(size)) == 0;
#else #else
const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0; const auto set_size_result = ftruncate(fileno(file), s64(size)) == 0;
#endif #endif
if (!set_size_result) { if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()}; const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message()); PathToUTF8String(file_path), size, ec.message());
} }
return set_size_result; return set_size_result;
} }
return false;
}
u64 IOFile::GetSize() const { u64 IOFile::GetSize() const {
if (!IsOpen()) { if (IsMappedFile())
return 0; return mmap_size;
} if (file) {
// Flush any unwritten buffered data into the file prior to retrieving the file mmap_size.
// Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file); std::fflush(file);
#if __ANDROID__
#ifdef __ANDROID__
u64 file_size = 0; u64 file_size = 0;
if (Android::IsContentUri(file_path)) { if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path); file_size = Android::GetSize(file_path);
@@ -406,54 +518,64 @@ u64 IOFile::GetSize() const {
file_size = fs::file_size(file_path, ec); file_size = fs::file_size(file_path, ec);
if (ec) { if (ec) {
LOG_ERROR(Common_Filesystem, LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
"Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
return 0; return 0;
} }
} }
#else #else
std::error_code ec; std::error_code ec;
auto const file_size = fs::file_size(file_path, ec);
const auto file_size = fs::file_size(file_path, ec);
if (ec) { if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message()); PathToUTF8String(file_path), ec.message());
return 0; return 0;
} }
#endif #endif
return file_size; return file_size;
} }
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
if (!IsOpen()) {
return false;
}
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem,
"Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return seek_result;
}
s64 IOFile::Tell() const {
if (!IsOpen()) {
return 0; return 0;
} }
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
if (IsMappedFile()) {
// fuck you to whoever made this method const
switch (origin) {
case SeekOrigin::SetOrigin:
mmap_offset = s64(offset);
break;
case SeekOrigin::CurrentPosition:
mmap_offset += s64(offset);
break;
case SeekOrigin::End:
mmap_offset = s64(mmap_size) + s64(offset);
break;
}
return true;
}
if (file) {
errno = 0; errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return seek_result;
}
return false;
}
s64 IOFile::Tell() const {
if (IsMappedFile()) {
errno = 0;
return s64(mmap_offset);
}
if (file) {
errno = 0;
return ftello(file); return ftello(file);
} }
return 0;
}
} // namespace Common::FS } // namespace Common::FS
+45 -37
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include <cstdio> #include <cstdio>
#include <cstring>
#include <filesystem> #include <filesystem>
#include <span> #include <span>
#include <type_traits> #include <type_traits>
@@ -186,19 +187,6 @@ public:
FileType type = FileType::BinaryFile, FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly); FileShareFlag flag = FileShareFlag::ShareReadOnly);
// #ifdef _WIN32
// template <typename Path>
// void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
// FileShareFlag flag = FileShareFlag::ShareReadOnly) {
// using ValueType = typename Path::value_type;
// if constexpr (IsChar<ValueType>) {
// Open(ToU8String(path), mode, type, flag);
// } else {
// Open(std::filesystem::path{path}, mode, type, flag);
// }
// }
// #endif
/// Closes the file if it is opened. /// Closes the file if it is opened.
void Close(); void Close();
@@ -228,8 +216,7 @@ public:
[[nodiscard]] size_t Read(T& data) const { [[nodiscard]] size_t Read(T& data) const {
if constexpr (IsContiguousContainer<T>) { if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type; using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>, static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
"Data type must be trivially copyable.");
return ReadSpan<ContiguousType>(data); return ReadSpan<ContiguousType>(data);
} else { } else {
return ReadObject(data) ? 1 : 0; return ReadObject(data) ? 1 : 0;
@@ -254,8 +241,7 @@ public:
[[nodiscard]] size_t Write(const T& data) const { [[nodiscard]] size_t Write(const T& data) const {
if constexpr (IsContiguousContainer<T>) { if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type; using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>, static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
"Data type must be trivially copyable.");
return WriteSpan<ContiguousType>(data); return WriteSpan<ContiguousType>(data);
} else { } else {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
@@ -282,10 +268,11 @@ public:
template <typename T> template <typename T>
requires std::is_trivially_copyable_v<T> requires std::is_trivially_copyable_v<T>
[[nodiscard]] size_t ReadSpan(std::span<T> data) const { [[nodiscard]] size_t ReadSpan(std::span<T> data) const {
if (!IsOpen()) { if (IsMappedFile()) {
return 0; std::memcpy(data.data(), mmap_base + mmap_offset, sizeof(T) * data.size());
return data.size();
} }
return std::fread(data.data(), sizeof(T), data.size(), file); return IsOpen() ? std::fread(data.data(), sizeof(T), data.size(), file) : 0;
} }
/** /**
@@ -306,12 +293,11 @@ public:
template <typename T> template <typename T>
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const { [[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (IsMappedFile()) {
if (!IsOpen()) { std::memcpy(mmap_base + mmap_offset, data.data(), sizeof(T) * data.size());
return 0; return data.size();
} }
return IsOpen() ? std::fwrite(data.data(), sizeof(T), data.size(), file) : 0;
return std::fwrite(data.data(), sizeof(T), data.size(), file);
} }
/** /**
@@ -334,12 +320,15 @@ public:
[[nodiscard]] bool ReadObject(T& object) const { [[nodiscard]] bool ReadObject(T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object."); static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (IsMappedFile()) {
if (!IsOpen()) { std::memcpy(&object, mmap_base + mmap_offset, sizeof(T));
return false; #ifdef _WIN32
return bool(sizeof(T));
#else
return sizeof(T);
#endif
} }
return IsOpen() ? std::fread(&object, sizeof(T), 1, file) == 1 : false;
return std::fread(&object, sizeof(T), 1, file) == 1;
} }
/** /**
@@ -361,12 +350,15 @@ public:
[[nodiscard]] bool WriteObject(const T& object) const { [[nodiscard]] bool WriteObject(const T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object."); static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (IsMappedFile()) {
if (!IsOpen()) { std::memcpy(mmap_base + mmap_offset, &object, sizeof(T));
return false; #ifdef _WIN32
return sizeof(T) != 0;
#else
return sizeof(T);
#endif
} }
return IsOpen() ? std::fwrite(&object, sizeof(T), 1, file) == 1 : false;
return std::fwrite(&object, sizeof(T), 1, file) == 1;
} }
/** /**
@@ -449,12 +441,28 @@ public:
*/ */
[[nodiscard]] s64 Tell() const; [[nodiscard]] s64 Tell() const;
private: #ifdef _WIN32
inline bool IsMappedFile() const noexcept { return mapping_handle != nullptr; }
#else // POSIX
inline bool IsMappedFile() const noexcept { return mmap_fd != -1; }
#endif
std::filesystem::path file_path; std::filesystem::path file_path;
FileAccessMode file_access_mode{}; FileAccessMode file_access_mode{};
FileType file_type{}; FileType file_type{};
std::FILE* file = nullptr; std::FILE* file = nullptr;
// Any decent system should have mmap() for files
// Systems with artifical mmap() limitations should simply change the logic within file.cpp
// and reduce the threshold for which the mmap() is set to
#ifdef _WIN32
void *mapping_handle = nullptr;
void *file_handle = nullptr;
#else // POSIX
int mmap_fd = -1;
#endif
u8* mmap_base = nullptr;
size_t mmap_size = 0;
mutable s64 mmap_offset = 0; // fuck you
}; };
} // namespace Common::FS } // namespace Common::FS
+108 -6
View File
@@ -116,19 +116,121 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
} }
std::string UTF16ToUTF8(std::u16string_view input) { std::string UTF16ToUTF8(std::u16string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; std::string result;
return convert.to_bytes(input.data(), input.data() + input.size()); result.reserve(input.size() * 4);
for (size_t i = 0; i < input.size(); ++i) {
uint32_t code = input[i];
// Handle surrogate pairs
if (code >= 0xD800 && code <= 0xDBFF) {
if (i + 1 < input.size()) {
uint32_t low = input[i + 1];
if (low >= 0xDC00 && low <= 0xDFFF) {
code = ((code - 0xD800) << 10) + (low - 0xDC00) + 0x10000;
++i;
}
}
}
if (code <= 0x7F) {
result.push_back(char(code));
} else if (code <= 0x7FF) {
result.push_back(char(0xC0 | (code >> 6)));
result.push_back(char(0x80 | (code & 0x3F)));
} else if (code <= 0xFFFF) {
result.push_back(char(0xE0 | (code >> 12)));
result.push_back(char(0x80 | ((code >> 6) & 0x3F)));
result.push_back(char(0x80 | (code & 0x3F)));
} else {
result.push_back(char(0xF0 | (code >> 18)));
result.push_back(char(0x80 | ((code >> 12) & 0x3F)));
result.push_back(char(0x80 | ((code >> 6) & 0x3F)));
result.push_back(char(0x80 | (code & 0x3F)));
}
}
return result;
} }
std::u16string UTF8ToUTF16(std::string_view input) { std::u16string UTF8ToUTF16(std::string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; std::u16string result;
return convert.from_bytes(input.data(), input.data() + input.size()); result.reserve(input.size() * 2);
for (size_t i = 0; i < input.size(); ) {
uint32_t code = 0;
unsigned char c = input[i];
size_t len = 0;
if ((c & 0x80) == 0) {
code = c;
len = 1;
} else if ((c & 0xE0) == 0xC0) {
code = c & 0x1F;
len = 2;
} else if ((c & 0xF0) == 0xE0) {
code = c & 0x0F;
len = 3;
} else if ((c & 0xF8) == 0xF0) {
code = c & 0x07;
len = 4;
} else {
++i;
continue;
}
if (i + len - 1 >= input.size())
break;
for (size_t j = 1; j <= len - 1; ++j) {
if ((input[i + j] & 0xC0) != 0x80) {
code = 0xFFFD;
break;
}
code = (code << 6) | (input[i + j] & 0x3F);
}
if (code <= 0xFFFF) {
result.push_back(char16_t(code));
} else {
code -= 0x10000;
result.push_back(char16_t(0xD800 + (code >> 10)));
result.push_back(char16_t(0xDC00 + (code & 0x3FF)));
}
i += len;
}
return result;
} }
std::u32string UTF8ToUTF32(std::string_view input) { std::u32string UTF8ToUTF32(std::string_view input) {
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; std::u32string result;
return convert.from_bytes(input.data(), input.data() + input.size()); result.reserve(input.size());
for (size_t i = 0; i < input.size(); ) {
uint32_t code = 0;
unsigned char c = input[i];
size_t len = 0;
if ((c & 0x80) == 0) {
code = c;
len = 1;
} else if ((c & 0xE0) == 0xC0) {
code = c & 0x1F;
len = 2;
} else if ((c & 0xF0) == 0xE0) {
code = c & 0x0F;
len = 3;
} else if ((c & 0xF8) == 0xF0) {
code = c & 0x07;
len = 4;
} else {
++i;
continue;
} }
if (i + len - 1 >= input.size())
break;
for (size_t j = 1; j <= len - 1; ++j) {
if ((input[i + j] & 0xC0) != 0x80) {
code = 0xFFFD;
break;
}
code = (code << 6) | (input[i + j] & 0x3F);
}
result.push_back(code);
i += len;
}
return result;
}
#ifdef _WIN32 #ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, std::string_view input) { static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
@@ -13,7 +13,7 @@
#include <winsock2.h> #include <winsock2.h>
#include <windows.h> #include <windows.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#elif defined(__linux__) || defined(__ANDROID__) #elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
#include <cerrno> #include <cerrno>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <net/if.h> #include <net/if.h>
@@ -33,6 +33,13 @@
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
// Darwin doesn't define this for some very odd reason
// See https://stackoverflow.com/questions/5390164/getting-routing-table-on-macosx-programmatically
#ifndef SA_SIZE
#define SA_SIZE(sa) \
((!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? sizeof(long) \
: 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
#endif #endif
#include "common/common_types.h" #include "common/common_types.h"
@@ -104,7 +111,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#else #else
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() { std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__) #if defined(__ANDROID__) || defined(__linux__) || defined(__APPLE__) || defined(__managarm__)
struct ifaddrs* ifaddr = nullptr; struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) { if (getifaddrs(&ifaddr) != 0) {
LOG_ERROR(Network, "getifaddrs: {}", std::strerror(errno)); LOG_ERROR(Network, "getifaddrs: {}", std::strerror(errno));
@@ -119,8 +126,9 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
u32 flags; u32 flags;
}; };
std::vector<RoutingEntry> routes{}; std::vector<RoutingEntry> routes{};
#ifdef __ANDROID__ #if defined(__ANDROID__) || defined(__APPLE__)
// Even through Linux based, we can't reliably obtain routing information from there :( // Even through Linux based, we can't reliably obtain routing information from there :(
// macOS not Linux based and would murder us if we attempt to access /proc
#else #else
if (std::ifstream file("/proc/net/route"); file.is_open()) { if (std::ifstream file("/proc/net/route"); file.is_open()) {
file.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); //ignore header file.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); //ignore header
@@ -138,7 +146,10 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> ifaces; std::vector<Network::NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */ if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
// Apple hates human beings so lets pretend all families are fine
#if !defined(__APPLE__) && !defined(__managarm__)
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */ || ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
#endif
|| (ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) /* Not loopback */ || (ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) /* Not loopback */
continue; continue;
// Just use 0 as the gateway address if not found OR routes are empty :) // Just use 0 as the gateway address if not found OR routes are empty :)
@@ -158,7 +169,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return ifaces; return ifaces;
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__) || defined(__APPLE__)
std::vector<Network::NetworkInterface> ifaces; std::vector<Network::NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) { if (fd < 0) {
@@ -198,11 +209,15 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
if (msglen == 0 || msglen < SA_SIZE(sa)) if (msglen == 0 || msglen < SA_SIZE(sa))
break; break;
if (i == RTA_NETMASK && sa->sa_family == AF_LINK) { if (i == RTA_NETMASK && sa->sa_family == AF_LINK) {
size_t namelen = 0;
struct sockaddr_dl const* sdl = reinterpret_cast<struct sockaddr_dl const*>(sa); struct sockaddr_dl const* sdl = reinterpret_cast<struct sockaddr_dl const*>(sa);
#if defined(__FreeBSD__) && __FreeBSD__ < 15
iface.name = std::string{::link_ntoa(sdl)};
#else
size_t namelen = 0;
::link_ntoa_r(sdl, nullptr, &namelen); ::link_ntoa_r(sdl, nullptr, &namelen);
iface.name = std::string(namelen, ' '); iface.name = std::string(namelen, ' ');
::link_ntoa_r(sdl, iface.name.data(), &namelen); ::link_ntoa_r(sdl, iface.name.data(), &namelen);
#endif
std::memcpy(&iface.ip_address, sa, sizeof(struct sockaddr_in)); std::memcpy(&iface.ip_address, sa, sizeof(struct sockaddr_in));
} }
msglen -= SA_SIZE(sa); msglen -= SA_SIZE(sa);
+13 -66
View File
@@ -11,34 +11,15 @@ set(dynarmic_VERSION ${dynarmic_VERSION_MAJOR}.${dynarmic_VERSION_MINOR}.${dynar
project(dynarmic LANGUAGES C CXX ASM VERSION ${dynarmic_VERSION}) project(dynarmic LANGUAGES C CXX ASM VERSION ${dynarmic_VERSION})
# Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif()
if (MASTER_PROJECT)
include(CTest)
endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
if (OPENBSD OR DRAGONFLY OR NETBSD) if (OPENBSD OR DRAGONFLY OR NETBSD)
set(REQUIRE_WX ON) set(REQUIRE_WX ON)
else() else()
set(REQUIRE_WX OFF) set(REQUIRE_WX OFF)
endif() endif()
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX}) option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" ON)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF) option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF) CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF)
option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
# Set hard requirements for C++ # Set hard requirements for C++
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -88,7 +69,7 @@ if (MSVC)
-Wno-missing-braces) -Wno-missing-braces)
endif() endif()
else() else()
set(DYNARMIC_CXX_FLAGS list(APPEND DYNARMIC_CXX_FLAGS
-Wall -Wall
-Wextra -Wextra
-Wcast-qual -Wcast-qual
@@ -103,37 +84,35 @@ else()
# to Address& after first checking isMEM(), and that code is inlined in a situation where # to Address& after first checking isMEM(), and that code is inlined in a situation where
# GCC knows that the variable is actually a Reg64. isMEM() will never return true for a # GCC knows that the variable is actually a Reg64. isMEM() will never return true for a
# Reg64, but GCC doesn't know that. # Reg64, but GCC doesn't know that.
list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds) list(APPEND DYNARMIC_CXX_FLAGS
list(APPEND DYNARMIC_CXX_FLAGS -Wstack-usage=4096) -Wno-array-bounds
-Wstack-usage=4096)
endif() endif()
if (CXX_CLANG) if (CXX_CLANG)
list(APPEND DYNARMIC_CXX_FLAGS
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array. # And this in turns limits the size of a std::array.
list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024) -fbracket-depth=1024
# Clang mistakenly blames CMake for using unused arguments during compilation # Clang mistakenly blames CMake for using unused arguments during compilation
list(APPEND DYNARMIC_CXX_FLAGS -Wno-unused-command-line-argument) -Wno-unused-command-line-argument)
endif() endif()
endif() endif()
if (NOT Boost_FOUND) if (NOT Boost_FOUND)
find_package(Boost 1.57 REQUIRED) find_package(Boost 1.57 REQUIRED)
endif() endif()
find_package(fmt 8 CONFIG) find_package(fmt 8 CONFIG)
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS) if (ARCHITECTURE_arm64)
find_package(oaknut 2.0.1 CONFIG) find_package(oaknut 2.0.1 CONFIG)
endif() endif()
if (ARCHITECTURE_riscv64)
if ("riscv64" IN_LIST ARCHITECTURE)
find_package(biscuit 0.9.1 REQUIRED) find_package(biscuit 0.9.1 REQUIRED)
endif() endif()
if (ARCHITECTURE_loongarch64)
if ("loongarch64" IN_LIST ARCHITECTURE)
find_package(lagoon REQUIRED) find_package(lagoon REQUIRED)
endif() endif()
if (ARCHITECTURE_x86_64)
if ("x86_64" IN_LIST ARCHITECTURE)
find_package(xbyak 7 CONFIG) find_package(xbyak 7 CONFIG)
endif() endif()
@@ -142,44 +121,12 @@ if (DYNARMIC_USE_LLVM)
separate_arguments(LLVM_DEFINITIONS) separate_arguments(LLVM_DEFINITIONS)
endif() endif()
# Dynarmic project files
add_subdirectory(src/dynarmic)
if (DYNARMIC_TESTS) if (DYNARMIC_TESTS)
find_package(Catch2 3 CONFIG) find_package(Catch2 3 CONFIG)
if (DYNARMIC_TESTS_USE_UNICORN) if (DYNARMIC_TESTS_USE_UNICORN)
find_package(Unicorn REQUIRED) find_package(Unicorn REQUIRED)
endif() endif()
endif()
# Dynarmic project files
add_subdirectory(src/dynarmic)
if (DYNARMIC_TESTS)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
#
# Install
#
if (DYNARMIC_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
install(TARGETS dynarmic EXPORT dynarmicTargets)
install(EXPORT dynarmicTargets
NAMESPACE dynarmic::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
configure_package_config_file(CMakeModules/dynarmicConfig.cmake.in
dynarmicConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
write_basic_package_version_file(dynarmicConfigVersion.cmake
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
install(DIRECTORY src/dynarmic TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
endif()
@@ -1,29 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
function(target_architecture_specific_sources project arch)
if (NOT MULTIARCH_BUILD)
target_sources("${project}" PRIVATE ${ARGN})
return()
endif()
foreach(input_file IN LISTS ARGN)
if(input_file MATCHES ".cpp$")
if(NOT IS_ABSOLUTE ${input_file})
set(input_file "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}")
endif()
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/arch_gen/${input_file}")
add_custom_command(
OUTPUT "${output_file}"
COMMAND ${CMAKE_COMMAND} "-Darch=${arch}"
"-Dinput_file=${input_file}"
"-Doutput_file=${output_file}"
-P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/impl/TargetArchitectureSpecificSourcesWrapFile.cmake"
DEPENDS "${input_file}"
VERBATIM
)
target_sources(${project} PRIVATE "${output_file}")
endif()
endforeach()
endfunction()
@@ -1,31 +0,0 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set(ARCHITECTURE "@ARCHITECTURE@")
if (NOT @BUILD_SHARED_LIBS@)
find_dependency(Boost 1.57)
find_dependency(fmt 9)
find_dependency(mcl 0.1.12 EXACT)
if ("arm64" IN_LIST ARCHITECTURE)
find_dependency(oaknut 2.0.1)
endif()
if ("riscv" IN_LIST ARCHITECTURE)
find_dependency(biscuit 0.9.1)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
find_dependency(xbyak 7)
endif()
if (@DYNARMIC_USE_LLVM@)
find_dependency(LLVM)
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components(@PROJECT_NAME@)
@@ -1,6 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
string(TOUPPER "${arch}" arch)
file(READ "${input_file}" f_contents)
file(WRITE "${output_file}" "#if defined(ARCHITECTURE_${arch})\n${f_contents}\n#endif\n")
+6 -14
View File
@@ -1,7 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
include(TargetArchitectureSpecificSources)
add_library(dynarmic STATIC add_library(dynarmic STATIC
mcl/bit.hpp mcl/bit.hpp
mcl/function_info.hpp mcl/function_info.hpp
@@ -135,7 +133,7 @@ add_library(dynarmic STATIC
interface/A64/config.h interface/A64/config.h
) )
if ("x86_64" IN_LIST ARCHITECTURE) if (ARCHITECTURE_x86_64)
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently # Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
# fail in dynarmic # fail in dynarmic
target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0) target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0)
@@ -143,7 +141,7 @@ if ("x86_64" IN_LIST ARCHITECTURE)
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1) target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
target_link_libraries(dynarmic PRIVATE xbyak::xbyak) target_link_libraries(dynarmic PRIVATE xbyak::xbyak)
target_architecture_specific_sources(dynarmic "x86_64" target_sources(dynarmic PRIVATE
backend/x64/abi.cpp backend/x64/abi.cpp
backend/x64/abi.h backend/x64/abi.h
backend/x64/block_of_code.cpp backend/x64/block_of_code.cpp
@@ -201,10 +199,10 @@ if ("x86_64" IN_LIST ARCHITECTURE)
) )
endif() endif()
if ("arm64" IN_LIST ARCHITECTURE) if (ARCHITECTURE_arm64)
target_link_libraries(dynarmic PRIVATE merry::oaknut) target_link_libraries(dynarmic PRIVATE merry::oaknut)
target_architecture_specific_sources(dynarmic "arm64" target_sources(dynarmic PRIVATE
backend/arm64/a32_jitstate.cpp backend/arm64/a32_jitstate.cpp
backend/arm64/a32_jitstate.h backend/arm64/a32_jitstate.h
backend/arm64/a64_jitstate.h backend/arm64/a64_jitstate.h
@@ -255,7 +253,7 @@ if ("arm64" IN_LIST ARCHITECTURE)
) )
endif() endif()
if ("riscv64" IN_LIST ARCHITECTURE) if (ARCHITECTURE_riscv64)
target_link_libraries(dynarmic PRIVATE biscuit::biscuit) target_link_libraries(dynarmic PRIVATE biscuit::biscuit)
target_sources(dynarmic PRIVATE target_sources(dynarmic PRIVATE
@@ -295,7 +293,7 @@ if ("riscv64" IN_LIST ARCHITECTURE)
message(WARNING "TODO: Incomplete frontend for this host architecture") message(WARNING "TODO: Incomplete frontend for this host architecture")
endif() endif()
if ("loongarch64" IN_LIST ARCHITECTURE) if (ARCHITECTURE_loongarch64)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon) target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
target_sources(dynarmic PRIVATE target_sources(dynarmic PRIVATE
@@ -400,15 +398,9 @@ if (DYNARMIC_USE_LLVM)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_USE_LLVM=1 ${LLVM_DEFINITIONS}) target_compile_definitions(dynarmic PRIVATE DYNARMIC_USE_LLVM=1 ${LLVM_DEFINITIONS})
llvm_config(dynarmic USE_SHARED armdesc armdisassembler aarch64desc aarch64disassembler x86desc x86disassembler) llvm_config(dynarmic USE_SHARED armdesc armdisassembler aarch64desc aarch64disassembler x86desc x86disassembler)
endif() endif()
if (DYNARMIC_ENABLE_CPU_FEATURE_DETECTION)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_CPU_FEATURE_DETECTION=1)
endif()
if (DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT) if (DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=1) target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=1)
endif() endif()
if (DYNARMIC_IGNORE_ASSERTS)
target_compile_definitions(dynarmic PRIVATE MCL_IGNORE_ASSERTS=1)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(dynarmic PRIVATE FMT_USE_WINDOWS_H=0) target_compile_definitions(dynarmic PRIVATE FMT_USE_WINDOWS_H=0)
endif() endif()
@@ -201,7 +201,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const { void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
ir_block.Reset(descriptor); ir_block.Reset(descriptor);
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
Optimization::Optimize(ir_block, conf, {}); Optimization::Optimize(ir_block, conf, {});
} }
@@ -366,8 +366,7 @@ A64AddressSpace::A64AddressSpace(const A64::UserConfig& conf)
void A64AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const { void A64AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
ir_block.Reset(descriptor); ir_block.Reset(descriptor);
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }; A64::Translate(ir_block, A64::LocationDescriptor{descriptor}, conf);
A64::Translate(ir_block, A64::LocationDescriptor{descriptor}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
Optimization::Optimize(ir_block, conf, {}); Optimization::Optimize(ir_block, conf, {});
} }
@@ -21,7 +21,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
} }
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const { void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
Optimization::Optimize(ir_block, conf, {.sha256 = true}); Optimization::Optimize(ir_block, conf, {.sha256 = true});
} }
@@ -27,7 +27,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
} }
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const { void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf);
Optimization::Optimize(ir_block, conf, {}); Optimization::Optimize(ir_block, conf, {});
} }
@@ -197,15 +197,15 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHa
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal)) if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal)) if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal)) if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal)) if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal)) if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
UNREACHABLE(); UNREACHABLE();
} }
@@ -202,7 +202,7 @@ private:
// LocationDescriptor ctor() does important ops (like tflags) do not skip // LocationDescriptor ctor() does important ops (like tflags) do not skip
auto const arch_descriptor = A32::LocationDescriptor{descriptor}; auto const arch_descriptor = A32::LocationDescriptor{descriptor};
ir_block.Reset(arch_descriptor); ir_block.Reset(arch_descriptor);
A32::Translate(ir_block, arch_descriptor, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); A32::Translate(ir_block, arch_descriptor, conf);
Optimization::Optimize(ir_block, conf, polyfill_options); Optimization::Optimize(ir_block, conf, polyfill_options);
return emitter.Emit(ir_block); return emitter.Emit(ir_block);
} }
@@ -254,11 +254,10 @@ private:
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE); block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
// JIT Compile // JIT Compile
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
// LocationDescriptor ctor() does important ops (like tflags) do not skip // LocationDescriptor ctor() does important ops (like tflags) do not skip
auto const arch_descriptor = A64::LocationDescriptor{descriptor}; auto const arch_descriptor = A64::LocationDescriptor{descriptor};
ir_block.Reset(arch_descriptor); ir_block.Reset(arch_descriptor);
A64::Translate(ir_block, arch_descriptor, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct}); A64::Translate(ir_block, arch_descriptor, conf);
Optimization::Optimize(ir_block, conf, polyfill_options); Optimization::Optimize(ir_block, conf, polyfill_options);
return emitter.Emit(ir_block).entrypoint; return emitter.Emit(ir_block).entrypoint;
} }
@@ -78,7 +78,6 @@ void ProtectMemory(const void* base, size_t size, bool is_executable) {
static const HostFeature features = []() { static const HostFeature features = []() {
HostFeature f{}; HostFeature f{};
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
using Cpu = Xbyak::util::Cpu; using Cpu = Xbyak::util::Cpu;
Xbyak::util::Cpu cpu_info{}; Xbyak::util::Cpu cpu_info{};
if (cpu_info.has(Cpu::tSSSE3)) f |= HostFeature::SSSE3; if (cpu_info.has(Cpu::tSSSE3)) f |= HostFeature::SSSE3;
@@ -121,7 +120,6 @@ static const HostFeature features = []() {
} }
} }
return f; return f;
#endif
}(); }();
HostFeature GetHostFeatures() { HostFeature GetHostFeatures() {
return features; return features;
@@ -7,34 +7,27 @@
*/ */
#include <cstring> #include <cstring>
#include <cassert>
#include "common/assert.h"
#include "dynarmic/backend/x64/block_of_code.h" #include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/backend/x64/constant_pool.h" #include "dynarmic/backend/x64/constant_pool.h"
namespace Dynarmic::Backend::X64 { namespace Dynarmic::Backend::X64 {
ConstantPool::ConstantPool(BlockOfCode& code, size_t size) ConstantPool::ConstantPool(BlockOfCode& code, size_t size) noexcept {
: insertion_point(0) auto const align_size = 64; //always align to cacheline
{ code.EnsureMemoryCommitted(2 * align_size + size);
code.EnsureMemoryCommitted(align_size + size);
code.int3();
code.align(align_size); code.align(align_size);
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size); pool = std::span<u128>(static_cast<u128*>(code.AllocateFromCodeSpace(size)), size / sizeof(u128));
} }
Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper) { Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lo, u64 hi) noexcept {
const auto constant = ConstantT(lower, upper); // offset grows iff new constant is added
auto it = constant_info.find(constant); auto const key = std::make_pair(lo, hi);
if (it == constant_info.end()) { auto const val = u32(constant_info.size());
ASSERT(insertion_point < pool.size()); auto const [it, _] = constant_info.insert({key, val});
ConstantT& target_constant = pool[insertion_point]; // if it != constant_info.end(): assume(pool[it->second] == c);
target_constant = constant; pool[it->second] = u128{lo, hi};
it = constant_info.insert({constant, &target_constant}).first; return frame[code.rip + std::addressof(pool[it->second])];
++insertion_point;
}
return frame[code.rip + it->second];
} }
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64
@@ -12,6 +12,7 @@
#include <cstddef> #include <cstddef>
#include <span> #include <span>
#include <utility> #include <utility>
#include <boost/functional/hash.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/container/unordered_map.h" #include "common/container/unordered_map.h"
@@ -21,31 +22,17 @@ namespace Dynarmic::Backend::X64 {
class BlockOfCode; class BlockOfCode;
/// ConstantPool allocates a block of memory from BlockOfCode. /// @brief ConstantPool allocates a block of memory from BlockOfCode.
/// It places constants into this block of memory, returning the address /// It places constants into this block of memory, returning the address
/// of the memory location where the constant is placed. If the constant /// of the memory location where the constant is placed. If the constant
/// already exists, its memory location is reused. /// already exists, its memory location is reused.
class ConstantPool final { class ConstantPool final {
public: public:
ConstantPool(BlockOfCode& code, size_t size); ConstantPool(BlockOfCode& code, size_t size) noexcept;
Xbyak::Address GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0) noexcept;
Xbyak::Address GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0); // key = identity hash, value = offset from pool.data()
::Common::unordered_map<std::pair<u64, u64>, u32, boost::hash<std::pair<u64, u64>>> constant_info;
private: std::span<u128> pool;
static constexpr size_t align_size = 16; // bytes
using ConstantT = std::pair<u64, u64>;
static_assert(sizeof(ConstantT) == align_size);
struct ConstantHash {
std::size_t operator()(const ConstantT& constant) const noexcept {
return constant.first ^ std::rotl<u64>(constant.second, 1);
}
};
::Common::unordered_map<ConstantT, void*, ConstantHash> constant_info;
std::span<ConstantT> pool;
std::size_t insertion_point;
}; };
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64
@@ -275,6 +275,7 @@ const void* EmitReadMemoryMov(BlockOfCode& code, int value_idx, const Xbyak::Reg
code.xadd(qword[addr], Xbyak::Reg64(value_idx)); code.xadd(qword[addr], Xbyak::Reg64(value_idx));
break; break;
case 128: case 128:
ASSERT(Xbyak::Xmm(value_idx) != xmm0);
code.lock(); code.lock();
code.cmpxchg16b(xword[addr]); code.cmpxchg16b(xword[addr]);
if (code.HasHostFeature(HostFeature::SSE41)) { if (code.HasHostFeature(HostFeature::SSE41)) {
@@ -6226,9 +6226,7 @@ void EmitX64::EmitVectorZeroExtend64(EmitContext& ctx, IR::Inst* inst) {
void EmitX64::EmitVectorZeroUpper(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorZeroUpper(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]); auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
code.movq(a, a); // TODO: !IsLastUse code.movq(a, a); // TODO: !IsLastUse
ctx.reg_alloc.DefineValue(code, inst, a); ctx.reg_alloc.DefineValue(code, inst, a);
} }
@@ -14,12 +14,6 @@
#include "dynarmic/backend/x64/hostloc.h" #include "dynarmic/backend/x64/hostloc.h"
#include "dynarmic/common/spin_lock.h" #include "dynarmic/common/spin_lock.h"
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
#else
static const auto default_cg_mode = nullptr; //Allow RWE
#endif
namespace Dynarmic { namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) { void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
@@ -78,7 +72,13 @@ namespace {
struct SpinLockImpl { struct SpinLockImpl {
void Initialize() noexcept; void Initialize() noexcept;
static void GlobalInitialize() noexcept; static void GlobalInitialize() noexcept;
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode); Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
, Xbyak::DontSetProtectRWE
#else
, nullptr //Allow RWE
#endif
);
void (*lock)(volatile int*) = nullptr; void (*lock)(volatile int*) = nullptr;
void (*unlock)(volatile int*) = nullptr; void (*unlock)(volatile int*) = nullptr;
}; };
@@ -9,15 +9,16 @@
#include "dynarmic/frontend/A32/translate/a32_translate.h" #include "dynarmic/frontend/A32/translate/a32_translate.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h" #include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/basic_block.h"
namespace Dynarmic::A32 { namespace Dynarmic::A32 {
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); void TranslateArm(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { void Translate(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(block, descriptor, tcb, options); return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(block, descriptor, conf);
} }
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction); bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
@@ -10,6 +10,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/interface/A32/arch_version.h" #include "dynarmic/interface/A32/arch_version.h"
#include "dynarmic/interface/A32/config.h"
namespace Dynarmic::IR { namespace Dynarmic::IR {
class Block; class Block;
@@ -20,28 +21,14 @@ namespace Dynarmic::A32 {
class LocationDescriptor; class LocationDescriptor;
struct TranslateCallbacks; struct TranslateCallbacks;
struct TranslationOptions {
ArchVersion arch_version;
/// This changes what IR we emit when we translate an unpredictable instruction.
/// If this is false, the ExceptionRaised IR instruction is emitted.
/// If this is true, we define some behaviour for some instructions.
bool define_unpredictable_behaviour = false;
/// This changes what IR we emit when we translate a hint instruction.
/// If this is false, we treat the instruction as a NOP.
/// If this is true, we emit an ExceptionRaised instruction.
bool hook_hint_instructions = true;
};
/** /**
* This function translates instructions in memory into our intermediate representation. * This function translates instructions in memory into our intermediate representation.
* @param descriptor The starting location of the basic block. Includes information like PC, Thumb state, &c. * @param descriptor The starting location of the basic block. Includes information like PC, Thumb state, &c.
* @param tcb The callbacks we should use to read emulated memory. * @param tcb The callbacks we should use to read emulated memory.
* @param options Configures how certain instructions are translated. * @param conf Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation. * @return A translated basic block in the intermediate representation.
*/ */
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); void Translate(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf);
/** /**
* This function translates a single provided instruction into our intermediate representation. * This function translates a single provided instruction into our intermediate representation.
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage * Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@@ -10,7 +13,7 @@ namespace Dynarmic::A32 {
// BKPT #<imm16> // BKPT #<imm16>
bool TranslatorVisitor::arm_BKPT(Cond cond, Imm<12> /*imm12*/, Imm<4> /*imm4*/) { bool TranslatorVisitor::arm_BKPT(Cond cond, Imm<12> /*imm12*/, Imm<4> /*imm4*/) {
if (cond != Cond::AL && !options.define_unpredictable_behaviour) { if (cond != Cond::AL && !conf.define_unpredictable_behaviour) {
return UnpredictableInstruction(); return UnpredictableInstruction();
} }
// UNPREDICTABLE: The instruction executes conditionally. // UNPREDICTABLE: The instruction executes conditionally.
@@ -9,6 +9,7 @@
#pragma once #pragma once
#include "common/assert.h" #include "common/assert.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/mcl/bit.hpp" #include "dynarmic/mcl/bit.hpp"
#include "dynarmic/frontend/A32/a32_ir_emitter.h" #include "dynarmic/frontend/A32/a32_ir_emitter.h"
@@ -25,12 +26,14 @@ enum class Exception;
struct TranslatorVisitor final { struct TranslatorVisitor final {
using instruction_return_type = bool; using instruction_return_type = bool;
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf)
: ir(block, descriptor, options.arch_version), options(options) {} : ir(block, descriptor, conf.arch_version)
, conf(conf)
{}
A32::IREmitter ir; A32::IREmitter ir;
ConditionalState cond_state = ConditionalState::None; ConditionalState cond_state = ConditionalState::None;
TranslationOptions options; A32::UserConfig const& conf;
size_t current_instruction_size; size_t current_instruction_size;
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage * Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@@ -9,7 +12,7 @@
namespace Dynarmic::A32 { namespace Dynarmic::A32 {
bool TranslatorVisitor::arm_PLD_imm(bool /*add*/, bool R, Reg /*n*/, Imm<12> /*imm12*/) { bool TranslatorVisitor::arm_PLD_imm(bool /*add*/, bool R, Reg /*n*/, Imm<12> /*imm12*/) {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -18,7 +21,7 @@ bool TranslatorVisitor::arm_PLD_imm(bool /*add*/, bool R, Reg /*n*/, Imm<12> /*i
} }
bool TranslatorVisitor::arm_PLD_reg(bool /*add*/, bool R, Reg /*n*/, Imm<5> /*imm5*/, ShiftType /*shift*/, Reg /*m*/) { bool TranslatorVisitor::arm_PLD_reg(bool /*add*/, bool R, Reg /*n*/, Imm<5> /*imm5*/, ShiftType /*shift*/, Reg /*m*/) {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -27,7 +30,7 @@ bool TranslatorVisitor::arm_PLD_reg(bool /*add*/, bool R, Reg /*n*/, Imm<5> /*im
} }
bool TranslatorVisitor::arm_SEV() { bool TranslatorVisitor::arm_SEV() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -35,7 +38,7 @@ bool TranslatorVisitor::arm_SEV() {
} }
bool TranslatorVisitor::arm_SEVL() { bool TranslatorVisitor::arm_SEVL() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -43,7 +46,7 @@ bool TranslatorVisitor::arm_SEVL() {
} }
bool TranslatorVisitor::arm_WFE() { bool TranslatorVisitor::arm_WFE() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -51,7 +54,7 @@ bool TranslatorVisitor::arm_WFE() {
} }
bool TranslatorVisitor::arm_WFI() { bool TranslatorVisitor::arm_WFI() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -59,7 +62,7 @@ bool TranslatorVisitor::arm_WFI() {
} }
bool TranslatorVisitor::arm_YIELD() { bool TranslatorVisitor::arm_YIELD() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
@@ -642,7 +642,7 @@ bool TranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
// SEV<c> // SEV<c>
bool TranslatorVisitor::thumb16_SEV() { bool TranslatorVisitor::thumb16_SEV() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::SendEvent); return RaiseException(Exception::SendEvent);
@@ -650,7 +650,7 @@ bool TranslatorVisitor::thumb16_SEV() {
// SEVL<c> // SEVL<c>
bool TranslatorVisitor::thumb16_SEVL() { bool TranslatorVisitor::thumb16_SEVL() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::SendEventLocal); return RaiseException(Exception::SendEventLocal);
@@ -658,7 +658,7 @@ bool TranslatorVisitor::thumb16_SEVL() {
// WFE<c> // WFE<c>
bool TranslatorVisitor::thumb16_WFE() { bool TranslatorVisitor::thumb16_WFE() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::WaitForEvent); return RaiseException(Exception::WaitForEvent);
@@ -666,7 +666,7 @@ bool TranslatorVisitor::thumb16_WFE() {
// WFI<c> // WFI<c>
bool TranslatorVisitor::thumb16_WFI() { bool TranslatorVisitor::thumb16_WFI() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::WaitForInterrupt); return RaiseException(Exception::WaitForInterrupt);
@@ -674,7 +674,7 @@ bool TranslatorVisitor::thumb16_WFI() {
// YIELD<c> // YIELD<c>
bool TranslatorVisitor::thumb16_YIELD() { bool TranslatorVisitor::thumb16_YIELD() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::Yield); return RaiseException(Exception::Yield);
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2021 MerryMage * Copyright (c) 2021 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@@ -8,7 +11,7 @@
namespace Dynarmic::A32 { namespace Dynarmic::A32 {
static bool PLDHandler(TranslatorVisitor& v, bool W) { static bool PLDHandler(TranslatorVisitor& v, bool W) {
if (!v.options.hook_hint_instructions) { if (!v.conf.hook_hint_instructions) {
return true; return true;
} }
@@ -18,7 +21,7 @@ static bool PLDHandler(TranslatorVisitor& v, bool W) {
} }
static bool PLIHandler(TranslatorVisitor& v) { static bool PLIHandler(TranslatorVisitor& v) {
if (!v.options.hook_hint_instructions) { if (!v.conf.hook_hint_instructions) {
return true; return true;
} }
@@ -22,10 +22,12 @@
namespace Dynarmic::A32 { namespace Dynarmic::A32 {
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { void TranslateArm(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
const bool single_step = descriptor.SingleStepping(); const bool single_step = descriptor.SingleStepping();
TranslatorVisitor visitor{block, descriptor, options}; TranslatorVisitor visitor{block, descriptor, conf};
bool should_continue = true; bool should_continue = true;
auto tcb = conf.callbacks;
do { do {
const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_pc = visitor.ir.current_location.PC();
u64 ticks_for_instruction = 1; u64 ticks_for_instruction = 1;
@@ -103,10 +103,12 @@ inline bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) noexcept {
} // namespace } // namespace
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, const A32::UserConfig& conf) {
const bool single_step = descriptor.SingleStepping(); const bool single_step = descriptor.SingleStepping();
TranslatorVisitor visitor{block, descriptor, options}; TranslatorVisitor visitor{block, descriptor, conf};
bool should_continue = true; bool should_continue = true;
auto tcb = conf.callbacks;
do { do {
const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_pc = visitor.ir.current_location.PC();
u64 ticks_for_instruction = 1; u64 ticks_for_instruction = 1;
@@ -11,19 +11,20 @@
#include "dynarmic/frontend/A64/a64_location_descriptor.h" #include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/frontend/A64/decoder/a64.h" #include "dynarmic/frontend/A64/decoder/a64.h"
#include "dynarmic/frontend/A64/translate/impl/impl.h" #include "dynarmic/frontend/A64/translate/impl/impl.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/terminal.h" #include "dynarmic/ir/terminal.h"
namespace Dynarmic::A64 { namespace Dynarmic::A64 {
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) { void Translate(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf) {
const bool single_step = descriptor.SingleStepping(); const bool single_step = descriptor.SingleStepping();
TranslatorVisitor visitor{block, descriptor, std::move(options)}; TranslatorVisitor visitor{block, descriptor, conf};
bool should_continue = true; bool should_continue = true;
do { do {
const u64 pc = visitor.ir.current_location->PC(); const u64 pc = visitor.ir.current_location->PC();
if (const auto instruction = memory_read_code(pc)) { if (const auto instruction = conf.callbacks->MemoryReadCode(pc)) {
if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) { if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) {
should_continue = *decoder; should_continue = *decoder;
} else { } else {
@@ -11,6 +11,7 @@
#include <optional> #include <optional>
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/interface/A64/config.h"
namespace Dynarmic { namespace Dynarmic {
@@ -22,32 +23,14 @@ namespace A64 {
class LocationDescriptor; class LocationDescriptor;
using MemoryReadCodeFuncType = std::function<std::optional<u32>(u64 vaddr)>;
struct TranslationOptions {
/// This changes what IR we emit when we translate an unpredictable instruction.
/// If this is false, the ExceptionRaised IR instruction is emitted.
/// If this is true, we define some behaviour for some instructions.
bool define_unpredictable_behaviour = false;
/// This tells the translator a wall clock will be used, thus allowing it
/// to avoid writting certain unnecessary code only needed for cycle timers.
bool wall_clock_cntpct = false;
/// This changes what IR we emit when we translate a hint instruction.
/// If this is false, we treat the instruction as a NOP.
/// If this is true, we emit an ExceptionRaised instruction.
bool hook_hint_instructions = true;
};
/** /**
* This function translates instructions in memory into our intermediate representation. * This function translates instructions in memory into our intermediate representation.
* @param descriptor The starting location of the basic block. Includes information like PC, FPCR state, &c. * @param descriptor The starting location of the basic block. Includes information like PC, FPCR state, &c.
* @param memory_read_code The function we should use to read emulated memory. * @param memory_read_code The function we should use to read emulated memory.
* @param options Configures how certain instructions are translated. * @param conf Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation. * @return A translated basic block in the intermediate representation.
*/ */
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options); void Translate(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf);
/** /**
* This function translates a single provided instruction into our intermediate representation. * This function translates a single provided instruction into our intermediate representation.
@@ -15,17 +15,20 @@
#include "dynarmic/frontend/A64/a64_types.h" #include "dynarmic/frontend/A64/a64_types.h"
#include "dynarmic/frontend/A64/translate/a64_translate.h" #include "dynarmic/frontend/A64/translate/a64_translate.h"
#include "dynarmic/frontend/imm.h" #include "dynarmic/frontend/imm.h"
#include "dynarmic/interface/A64/config.h"
namespace Dynarmic::A64 { namespace Dynarmic::A64 {
struct TranslatorVisitor final { struct TranslatorVisitor final {
using instruction_return_type = bool; using instruction_return_type = bool;
explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, TranslationOptions options) explicit TranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, A64::UserConfig const& conf)
: ir(block, descriptor), options(std::move(options)) {} : ir(block, descriptor)
, conf(conf)
{}
A64::IREmitter ir; A64::IREmitter ir;
TranslationOptions options; A64::UserConfig const& conf;
bool UnpredictableInstruction(); bool UnpredictableInstruction();
bool DecodeError(); bool DecodeError();
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage * Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@@ -25,7 +28,7 @@ static bool ExclusiveSharedDecodeAndOperation(TranslatorVisitor& v, bool pair, s
if (memop == IR::MemOp::LOAD && pair && Rt == *Rt2) { if (memop == IR::MemOp::LOAD && pair && Rt == *Rt2) {
return v.UnpredictableInstruction(); return v.UnpredictableInstruction();
} else if (memop == IR::MemOp::STORE && (*Rs == Rt || (pair && *Rs == *Rt2))) { } else if (memop == IR::MemOp::STORE && (*Rs == Rt || (pair && *Rs == *Rt2))) {
if (!v.options.define_unpredictable_behaviour) { if (!v.conf.define_unpredictable_behaviour) {
return v.UnpredictableInstruction(); return v.UnpredictableInstruction();
} }
// UNPREDICTABLE: The Constraint_NONE case is executed. // UNPREDICTABLE: The Constraint_NONE case is executed.
@@ -42,35 +42,35 @@ bool TranslatorVisitor::NOP() {
} }
bool TranslatorVisitor::YIELD() { bool TranslatorVisitor::YIELD() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::Yield); return RaiseException(Exception::Yield);
} }
bool TranslatorVisitor::WFE() { bool TranslatorVisitor::WFE() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::WaitForEvent); return RaiseException(Exception::WaitForEvent);
} }
bool TranslatorVisitor::WFI() { bool TranslatorVisitor::WFI() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::WaitForInterrupt); return RaiseException(Exception::WaitForInterrupt);
} }
bool TranslatorVisitor::SEV() { bool TranslatorVisitor::SEV() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::SendEvent); return RaiseException(Exception::SendEvent);
} }
bool TranslatorVisitor::SEVL() { bool TranslatorVisitor::SEVL() {
if (!options.hook_hint_instructions) { if (!conf.hook_hint_instructions) {
return true; return true;
} }
return RaiseException(Exception::SendEventLocal); return RaiseException(Exception::SendEventLocal);
@@ -129,7 +129,7 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3
return true; return true;
case SystemRegisterEncoding::CNTPCT_EL0: case SystemRegisterEncoding::CNTPCT_EL0:
// HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date. // HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date.
if (!ir.block.instructions.empty() && !options.wall_clock_cntpct) { if (!ir.block.instructions.empty() && !conf.wall_clock_cntpct) {
ir.block.CycleCount()--; ir.block.CycleCount()--;
ir.SetTerm(IR::Term::LinkBlock{*ir.current_location}); ir.SetTerm(IR::Term::LinkBlock{*ir.current_location});
return false; return false;
@@ -91,7 +91,7 @@ struct detail {
shifts[arg_index] = bit_position; shifts[arg_index] = bit_position;
} }
} }
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__) #if !defined(__ANDROID__)
// Avoids a MSVC ICE, and avoids Android NDK issue. // Avoids a MSVC ICE, and avoids Android NDK issue.
DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
#endif #endif
+1 -1
View File
@@ -180,7 +180,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32::UserConf
while (num_insts < instructions_to_execute_count) { while (num_insts < instructions_to_execute_count) {
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}}; A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
ir_block.Reset(descriptor); ir_block.Reset(descriptor);
A32::Translate(ir_block, descriptor, &test_env, {}); A32::Translate(ir_block, descriptor, config);
Optimization::Optimize(ir_block, config, {}); Optimization::Optimize(ir_block, config, {});
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str()); printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
printf("\n\nx86_64:\n"); printf("\n\nx86_64:\n");
+63
View File
@@ -2632,3 +2632,66 @@ TEST_CASE("A64: Rounding", "[a64]") {
CHECK(jit.GetVector(5) == Vector{0x4000000040000000, 0x4000000040000000}); CHECK(jit.GetVector(5) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(6) == Vector{0x4000000040000000, 0x4000000040000000}); CHECK(jit.GetVector(6) == Vector{0x4000000040000000, 0x4000000040000000});
} }
TEST_CASE("A64: SM4", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM4EKEY(V4.S4(), V0.S4(), V1.S4());
code.SM4E(V5.S4(), V0.S4(), V1.S4());
jit.SetPC(0);
jit.SetVector(0, {0xefcdab8967452301, 0x1032547698badcfe});
jit.SetVector(1, {0xefcdab8967452301, 0x1032547698badcfe});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(4) == Vector{0x486a0c2e00224466, 0x5c7e183a62402604});
CHECK(jit.GetVector(5) == Vector{0xc2e086a43c1e785a, 0xd7f593b104264062});
}
TEST_CASE("A64: SM3", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
SECTION("SM3SS1") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3SS1(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x0000000000000000, 0x73d67cd700000000});
}
SECTION("SM3PARTW1") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3PARTW1(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x653c64995693280c, 0xabcc7db97adebf12});
}
SECTION("SM3PARTW2") {
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.SM3PARTW2(V0.S4(), V0.S4(), V1.S4(), V2.S4());
jit.SetPC(0);
jit.SetVector(0, {0xec4aff517369c667, 0x46e3fbf2abbacd29});
jit.SetVector(1, {0x8de7e81bf854c27c, 0x9ac99f33632e5a76});
jit.SetVector(2, {0x5aa35831b70d3266, 0xd45ee95817055d25});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x30010fe70da43740, 0x6702425b4a3a05d4});
}
}
+1 -1
View File
@@ -268,7 +268,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
// const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); }; // const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
// const A64::LocationDescriptor location{instructions_start, FP::FPCR{fpcr}}; // const A64::LocationDescriptor location{instructions_start, FP::FPCR{fpcr}};
// IR::Block ir_block{location}; // IR::Block ir_block{location};
// A64::Translate(ir_block, location, get_code, {}); // A64::Translate(ir_block, location, config);
// fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block)); // fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
// Optimization::Optimize(ir_block, conf, {}); // Optimization::Optimize(ir_block, conf, {});
// fmt::print("Optimized IR:\n{}\n", IR::DumpBlock(ir_block)); // fmt::print("Optimized IR:\n{}\n", IR::DumpBlock(ir_block));
+3 -5
View File
@@ -1,7 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
include(TargetArchitectureSpecificSources)
add_executable(dynarmic_tests add_executable(dynarmic_tests
fp/FPToFixed.cpp fp/FPToFixed.cpp
fp/FPValue.cpp fp/FPValue.cpp
@@ -44,13 +42,13 @@ if (DYNARMIC_TESTS_USE_UNICORN)
) )
endif() endif()
if ("riscv" IN_LIST ARCHITECTURE) if (ARCHITECTURE_riscv64)
target_link_libraries(dynarmic_tests PRIVATE biscuit::biscuit) target_link_libraries(dynarmic_tests PRIVATE biscuit::biscuit)
endif() endif()
if ("x86_64" IN_LIST ARCHITECTURE) if (ARCHITECTURE_x86_64)
target_link_libraries(dynarmic_tests PRIVATE xbyak::xbyak) target_link_libraries(dynarmic_tests PRIVATE xbyak::xbyak)
target_architecture_specific_sources(dynarmic_tests "x86_64" target_sources(dynarmic_tests PRIVATE
x64_cpu_info.cpp x64_cpu_info.cpp
native/preserve_xmm.cpp native/preserve_xmm.cpp
) )
@@ -40,6 +40,8 @@ struct HostTranslateInfo {
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
///< control flow ///< control flow
bool single_lane_geometry_subgroups{}; bool single_lane_geometry_subgroups{};
bool support_attribute_texture_handle{}; ///< True when the host binds texture handles that
///< arrive as vertex attributes
void ApplyDescriptorLimitPolicy() noexcept { void ApplyDescriptorLimitPolicy() noexcept {
if (min_ssbo_alignment == 0) { if (min_ssbo_alignment == 0) {
+44 -3
View File
@@ -322,6 +322,10 @@ static inline u32 ReadCbufCached(Environment& env, u32 index, u32 offset) {
return v; return v;
} }
static inline bool IsAttributeHandle(const ConstBufferAddr& cbuf) {
return cbuf.index == ATTRIBUTE_HANDLE_CBUF_INDEX;
}
static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr& cbuf) { static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr& cbuf) {
// Must all be uniquely different variables // Must all be uniquely different variables
// If has secondary, then it will be cbuf.secondary_{index|offset}, else its 0. // If has secondary, then it will be cbuf.secondary_{index|offset}, else its 0.
@@ -341,14 +345,23 @@ static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr
// Cached variants of existing helpers // Cached variants of existing helpers
static inline TextureType ReadTextureTypeCached(Environment& env, const ConstBufferAddr& cbuf) { static inline TextureType ReadTextureTypeCached(Environment& env, const ConstBufferAddr& cbuf) {
if (IsAttributeHandle(cbuf)) {
return TextureType::Color2D;
}
return env.ReadTextureType(GetTextureHandleCached(env, cbuf)); return env.ReadTextureType(GetTextureHandleCached(env, cbuf));
} }
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env, static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
const ConstBufferAddr& cbuf) { const ConstBufferAddr& cbuf) {
if (IsAttributeHandle(cbuf)) {
return TexturePixelFormat::A8B8G8R8_UNORM;
}
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf)); return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
} }
static inline bool IsTexturePixelFormatIntegerCached(Environment& env, static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
const ConstBufferAddr& cbuf) { const ConstBufferAddr& cbuf) {
if (IsAttributeHandle(cbuf)) {
return false;
}
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf)); return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
} }
@@ -520,15 +533,43 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
}; };
} }
bool IsAttributeSourcedHandle(const IR::Value& handle) {
if (handle.IsImmediate()) {
return false;
}
const IR::Inst* inst{handle.InstRecursive()};
if (inst->GetOpcode() == IR::Opcode::BitCastU32F32 && !inst->Arg(0).IsImmediate()) {
inst = inst->Arg(0).InstRecursive();
}
return inst->GetOpcode() == IR::Opcode::GetAttribute ||
inst->GetOpcode() == IR::Opcode::GetAttributeU32;
}
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) { TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
ConstBufferAddr addr; ConstBufferAddr addr;
if (IsBindless(inst)) { if (IsBindless(inst)) {
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info, block)}; const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info, block)};
if (!track_addr) { if (track_addr) {
throw NotImplementedException("Failed to track bindless texture constant buffer");
} else {
addr = *track_addr; addr = *track_addr;
} else if (host_info.support_attribute_texture_handle &&
IsAttributeSourcedHandle(inst.Arg(0))) {
// The handle travels with the vertices (sprite batches pick the texture per quad), so
// there is no constant buffer to read it from. The host binds the handle of each run
// of vertices that share one.
addr = ConstBufferAddr{
.index = ATTRIBUTE_HANDLE_CBUF_INDEX,
.offset = 0,
.shift_left = 0,
.secondary_index = 0,
.secondary_offset = 0,
.secondary_shift_left = 0,
.dynamic_offset = {},
.count = 1,
.has_secondary = false,
};
} else {
throw NotImplementedException("Failed to track bindless texture constant buffer");
} }
} else { } else {
addr = ConstBufferAddr{ addr = ConstBufferAddr{
+4
View File
@@ -38,6 +38,10 @@ enum class TextureType : u32 {
}; };
constexpr u32 NUM_TEXTURE_TYPES = 9; constexpr u32 NUM_TEXTURE_TYPES = 9;
/// Constant buffer index marking a texture handle that the shader receives as a vertex attribute
/// instead of reading it from a constant buffer. The host supplies the handle of each draw run.
constexpr u32 ATTRIBUTE_HANDLE_CBUF_INDEX = 0xFFFF;
enum class TexturePixelFormat { enum class TexturePixelFormat {
A8B8G8R8_UNORM, A8B8G8R8_UNORM,
A8B8G8R8_SNORM, A8B8G8R8_SNORM,
@@ -276,6 +276,9 @@ GraphicsPipeline::GraphicsPipeline(
num_textures += Shader::NumDescriptors(info->texture_descriptors); num_textures += Shader::NumDescriptors(info->texture_descriptors);
num_image_elements += Shader::NumDescriptors(info->texture_descriptors); num_image_elements += Shader::NumDescriptors(info->texture_descriptors);
num_image_elements += Shader::NumDescriptors(info->image_descriptors); num_image_elements += Shader::NumDescriptors(info->image_descriptors);
for (const auto& desc : info->texture_descriptors) {
uses_attribute_handle |= desc.cbuf_index == Shader::ATTRIBUTE_HANDLE_CBUF_INDEX;
}
num_descriptor_entries += NumDescriptorEntries(*info); num_descriptor_entries += NumDescriptorEntries(*info);
} }
fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0]; fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0];
@@ -375,6 +378,9 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
} }
const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers}; const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers};
const auto read_handle{[&](const auto& desc, u32 index) { const auto read_handle{[&](const auto& desc, u32 index) {
if (desc.cbuf_index == Shader::ATTRIBUTE_HANDLE_CBUF_INDEX) {
return TexturePair(attribute_handle, via_header_index);
}
ASSERT(cbufs[desc.cbuf_index].enabled); ASSERT(cbufs[desc.cbuf_index].enabled);
const u32 index_offset{index << desc.size_shift}; const u32 index_offset{index << desc.size_shift};
const u32 offset{desc.cbuf_offset + index_offset}; const u32 offset{desc.cbuf_offset + index_offset};
@@ -110,6 +110,16 @@ public:
return configure_func(this, is_indexed); return configure_func(this, is_indexed);
} }
/// True when a shader samples a texture whose handle arrives as a vertex attribute.
[[nodiscard]] bool UsesAttributeHandle() const noexcept {
return uses_attribute_handle;
}
/// Handle bound to those textures by the next Configure.
void SetAttributeHandle(u32 handle) noexcept {
attribute_handle = handle;
}
[[nodiscard]] GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept { [[nodiscard]] GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept {
if (key == current_key) { if (key == current_key) {
return this; return this;
@@ -168,6 +178,8 @@ private:
u32 num_descriptor_entries{}; u32 num_descriptor_entries{};
size_t num_image_elements{}; size_t num_image_elements{};
u32 num_textures{}; u32 num_textures{};
u32 attribute_handle{};
bool uses_attribute_handle{};
bool fragment_has_color0_output{}; bool fragment_has_color0_output{};
vk::DescriptorSetLayout descriptor_set_layout; vk::DescriptorSetLayout descriptor_set_layout;
@@ -493,6 +493,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_conditional_barrier = device.SupportsConditionalBarriers(), .support_conditional_barrier = device.SupportsConditionalBarriers(),
.single_lane_geometry_subgroups = !profile.SupportsSubgroupStage(Shader::Stage::Geometry), .single_lane_geometry_subgroups = !profile.SupportsSubgroupStage(Shader::Stage::Geometry),
.support_attribute_texture_handle = true,
}; };
host_info.ApplyDescriptorLimitPolicy(); host_info.ApplyDescriptorLimitPolicy();
@@ -253,6 +253,15 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
// update engine as channel may be different. // update engine as channel may be different.
pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->SetEngine(maxwell3d, gpu_memory);
attribute_handle_pipeline = nullptr;
attribute_handle_runs.clear();
if (split_attribute_handles && pipeline->UsesAttributeHandle()) {
CollectAttributeHandleRuns(is_indexed);
if (!attribute_handle_runs.empty()) {
attribute_handle_pipeline = pipeline;
pipeline->SetAttributeHandle(attribute_handle_runs.front().handle);
}
}
if (!pipeline->Configure(is_indexed)) if (!pipeline->Configure(is_indexed))
return; return;
@@ -264,22 +273,110 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
draw_func(); draw_func();
} }
void RasterizerVulkan::CollectAttributeHandleRuns(bool is_indexed) {
using VertexAttribute = Maxwell::VertexAttribute;
const auto& regs = maxwell3d->regs;
const auto& draw_state = maxwell3d->draw_manager.draw_state;
const u32 total = is_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count;
if (total == 0) {
return;
}
// The handle is an integer attribute; sprite batches repeat it on every vertex of a quad.
const VertexAttribute* source{};
for (const auto& attr : regs.vertex_attrib_format) {
if (attr.constant || attr.size == VertexAttribute::Size::Invalid) {
continue;
}
if (attr.type == VertexAttribute::Type::UInt || attr.type == VertexAttribute::Type::SInt) {
source = &attr;
break;
}
}
const auto& stream = regs.vertex_streams[source ? source->buffer.Value() : 0];
if (!source || !stream.IsEnabled()) {
attribute_handle_runs.push_back({0, total, 0});
return;
}
// Only plain triangle lists are split; anything else keeps the handle of its first vertex.
const u32 primitive_size =
draw_state.topology == Maxwell::PrimitiveTopology::Triangles ? 3 : total;
const GPUVAddr base_address = stream.Address() + source->offset;
const auto read_handle = [&](u32 position) {
u32 vertex;
if (is_indexed) {
const auto& index_buffer = draw_state.index_buffer;
const GPUVAddr address =
index_buffer.IndexStart() + size_t{position} * index_buffer.FormatSizeInBytes();
u32 index{};
gpu_memory->ReadBlockUnsafe(address, &index, index_buffer.FormatSizeInBytes());
vertex = static_cast<u32>(draw_state.base_index + index);
} else {
vertex = draw_state.vertex_buffer.first + position;
}
return gpu_memory->Read<u32>(base_address + static_cast<GPUVAddr>(vertex) * stream.stride);
};
for (u32 position = 0; position + primitive_size <= total; position += primitive_size) {
const u32 handle = read_handle(position);
if (!attribute_handle_runs.empty() && attribute_handle_runs.back().handle == handle) {
attribute_handle_runs.back().count += primitive_size;
} else {
attribute_handle_runs.push_back({position, primitive_size, handle});
}
}
if (attribute_handle_runs.empty()) {
attribute_handle_runs.push_back({0, total, read_handle(0)});
} else {
const u32 covered = attribute_handle_runs.back().first + attribute_handle_runs.back().count;
if (covered < total) {
attribute_handle_runs.back().count += total - covered;
}
}
}
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
split_attribute_handles = true;
SCOPE_EXIT {
split_attribute_handles = false;
};
PrepareDraw(is_indexed, [this, is_indexed, instance_count] { PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
const auto& draw_state = maxwell3d->draw_manager.draw_state; const auto& draw_state = maxwell3d->draw_manager.draw_state;
const u32 num_instances{instance_count}; const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { const auto record_draw = [this](const DrawParams& params) {
if (draw_params.is_indexed) { scheduler.Record([params](vk::CommandBuffer cmdbuf) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, if (params.is_indexed) {
draw_params.first_index, draw_params.base_vertex, cmdbuf.DrawIndexed(params.num_vertices, params.num_instances,
draw_params.base_instance); params.first_index, params.base_vertex,
params.base_instance);
} else { } else {
cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, cmdbuf.Draw(params.num_vertices, params.num_instances, params.base_vertex,
draw_params.base_vertex, draw_params.base_instance); params.base_instance);
} }
}); });
};
if (attribute_handle_pipeline && attribute_handle_runs.size() > 1) {
// The first run is already configured, each further one binds its own texture.
for (size_t i = 0; i < attribute_handle_runs.size(); ++i) {
const AttributeHandleRun& run = attribute_handle_runs[i];
if (i != 0) {
attribute_handle_pipeline->SetAttributeHandle(run.handle);
if (!attribute_handle_pipeline->Configure(is_indexed)) {
continue;
}
}
DrawParams run_params = draw_params;
run_params.num_vertices = run.count;
if (is_indexed) {
run_params.first_index += run.first;
} else {
run_params.base_vertex += run.first;
}
record_draw(run_params);
}
} else {
record_draw(draw_params);
}
// Log draw call // Log draw call
if (GPU::Logging::IsActive() && if (GPU::Logging::IsActive() &&
@@ -157,6 +157,20 @@ private:
template <typename Func> template <typename Func>
void PrepareDraw(bool is_indexed, Func&&); void PrepareDraw(bool is_indexed, Func&&);
/// A stretch of a draw whose vertices all carry the same texture handle.
struct AttributeHandleRun {
u32 first;
u32 count;
u32 handle;
};
/// Splits the current draw wherever the texture handle carried in the vertex data changes.
void CollectAttributeHandleRuns(bool is_indexed);
std::vector<AttributeHandleRun> attribute_handle_runs;
GraphicsPipeline* attribute_handle_pipeline{};
bool split_attribute_handles{};
void FlushWork(); void FlushWork();
void UpdateDynamicStates(); void UpdateDynamicStates();
+1 -1
View File
@@ -128,7 +128,7 @@ li.checked::marker { content: &quot;\2612&quot;; }
<item> <item>
<widget class="QLabel" name="labelLinks"> <widget class="QLabel" name="labelLinks">
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://eden-emulator.github.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev/eden-emu/eden/activity/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://discord.gg/HstXbPch7X&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Discord&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://stt.gg/qKgFEAbH&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Stoat&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://nitter.poast.org/edenemuofficial&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Twitter&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://eden-emulator.github.io/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev/eden-emu/eden/activity/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://discord.gg/HstXbPch7X&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Discord&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://stt.gg/qKgFEAbH&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Stoat&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://x.com/edenemuofficial&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Twitter&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="openExternalLinks"> <property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>
+2 -1
View File
@@ -131,7 +131,8 @@ GRenderWindow::GRenderWindow(MainWindow* parent,
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
QGuiApplication::platformName() == QStringLiteral("wayland-egl"); QGuiApplication::platformName() == QStringLiteral("wayland-egl");
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &MainWindow::OnLoadComplete); connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &MainWindow::OnLoadComplete,
Qt::QueuedConnection);
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &MainWindow::OnExecuteProgram, connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &MainWindow::OnExecuteProgram,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(this, &GRenderWindow::ExitSignal, parent, &MainWindow::OnExit, Qt::QueuedConnection); connect(this, &GRenderWindow::ExitSignal, parent, &MainWindow::OnExit, Qt::QueuedConnection);
+4
View File
@@ -3147,6 +3147,10 @@ bool MainWindow::ConfirmShutdownGame() {
void MainWindow::OnLoadComplete() { void MainWindow::OnLoadComplete() {
loading_screen->OnLoadComplete(); loading_screen->OnLoadComplete();
if (emulation_running) {
render_window->show();
render_window->setFocus();
}
perf_overlay = new PerformanceOverlay(this); perf_overlay = new PerformanceOverlay(this);
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked()); perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
+5
View File
@@ -8,3 +8,8 @@ NPROC=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n
# shellcheck disable=SC2038 # shellcheck disable=SC2038
find . -type f -iname '*.png' | xargs -P "$NPROC" -I {} optipng -o7 {} find . -type f -iname '*.png' | xargs -P "$NPROC" -I {} optipng -o7 {}
which svgo || exit
# shellcheck disable=SC2038
find . -type f -iname '*.svg' | xargs -P "$NPROC" -I {} svgo {}