diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8d42a0ab27..bdf9d048e8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,4 @@ - [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). -- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- diff --git a/.gitignore b/.gitignore index 20ed5e93c1..873b99bec2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Build directory /[Bb]uild*/ doc-build/ +out/ AppDir/ uruntime diff --git a/AGENTS.md b/AGENTS.md index aabea77170..05ab7f1160 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1,7 @@ -AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase. +# AGENTS + +Read and follow the AI policy found in `docs/policies/AI.md`, and the coding policies found in `docs/policies/Coding.md`. + +Do not, under any circumstances, ever submit PRs or push commits on behalf of the user. + +Keep comments to a reasonable level. Do not overexplain or add/remove comments from unrelated parts of the codebase. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index aabea77170..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase. diff --git a/CMakeLists.txt b/CMakeLists.txt index 91a77b717b..f647204a4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,8 @@ cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSV option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF) option(ENABLE_WERROR "Enable -Werror diagnostics" ON) -# Lossless Scaling frame generation. -option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON) +# Lossless Scaling frame generation. Only Android. +cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF) option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON) diff --git a/CMakeModules/find/Findlz4.cmake b/CMakeModules/find/Findlz4.cmake index d4f2153162..77ac390160 100644 --- a/CMakeModules/find/Findlz4.cmake +++ b/CMakeModules/find/Findlz4.cmake @@ -24,7 +24,7 @@ else() endif() if (lz4_FOUND AND NOT TARGET lz4::lz4) - if (TARGET LZ4::lz4_shared) + if (TARGET LZ4::lz4_shared AND NOT YUZU_STATIC_BUILD) add_library(lz4::lz4 ALIAS LZ4::lz4_shared) elseif (TARGET LZ4::lz4_static) add_library(lz4::lz4 ALIAS LZ4::lz4_static) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c09b1c3679..c87dd11dbe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,5 @@ # Contributing - - Eden has lots of different ways that you can contribute to its efforts, even without knowing how to write code: - Donate! This will help us live our lives, pay for infrastructure/testing hardware, and more @@ -20,16 +16,14 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G ### Policies -- No LLM or AI usage, *period*, for patches, pull requests, issues, comments, debugging, brainstorming, etc. - - For details on why, see the [detailed AI policy](docs/policies/AI.md). -- Usage of any form of profanity or otherwise unsavory language is generally discouraged. - - This is primarily because it rarely helps to actually understand what's going on. - - Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best. -- New code must follow the same general style as the surrounding codebase. Exceptions may be granted in certain cases. +- 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. + - As a common courtesy, please disclose your AI usage under the PR description, as `Assisted-by: ()`. +- 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. - Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this. - You should generally respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem. - However, if you feel they are overstepping or are incorrect, don't be afraid to stand your ground! Maintainers are not always correct. -- While we do *not* adhere to the terms of a formal code of conduct, you will generally be expected to respect other developers, contributors, and community members. +- 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. ### Where do I start? @@ -43,7 +37,8 @@ You may also search for open issues on [GitHub](https://github.com/eden-emulator There are five primary ways to contribute. - Submit pull requests directly to our source tree - - You must first request an account on our Forgejo. See [Signup](#how-do-i-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. - 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. - Submit pull requests to our [Codeberg mirror](https://codeberg.org/eden-emu/eden) @@ -55,31 +50,8 @@ There are five primary ways to contribute. 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. -### How do I sign up? - -
-To sign up and begin contributing... (click to open) - -Email [crueter@crueter.xyz](mailto:crueter@crueter.xyz) with the following format: - -```txt -Subject: [Eden Git] Registration Request -Username: -Email: -I wish to sign up because... -``` - -Received mail that does not follow this format will be ignored. - -Once your request is processed, you will receive a confirmation email with your temporary password and some information on Git access and policies. If you do not receive a response within 48 hours, feel free to send another email. - -> [!WARNING] -> Some email providers may place the response email in your spam/junk folder; notable offenders include Gmail and Outlook. *Always* ensure to check your Spam or Junk folder. - -
- ## Non-code Contributions Alongside the other contribution methods listed up top, you can also choose to contribute through documentation, organization, or community guides. These can be done either through the code contribution methods described above, or created externally and shared via our Discord community. -If you have an external tool/page that you believe would be handy to integrate/link into Eden, please additionally email our developers at [`developers@eden-emu.dev`](mailto:developers@eden-emu.dev). **Do not submit vibe-coded or AI generated tools or applications**. +If you have an external tool/page that you believe would be handy to integrate/link into Eden, please additionally email our developers at [`developers@eden-emu.dev`](mailto:developers@eden-emu.dev). diff --git a/cpmfile.json b/cpmfile.json index f574bf5668..0032734712 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -79,7 +79,7 @@ "name": "ffmpeg", "package": "FFmpeg", "repo": "crueter-ci/FFmpeg", - "version": "9.0.1-1788120736-bf1b838f2a" + "version": "9.0.2-1790726865-946fcce07b" }, "fmt": { "hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2", @@ -333,6 +333,11 @@ "repo": "herumi/xbyak", "version": "v7.40.1" }, + "zbic": { + "hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482", + "repo": "kinnay/zbic", + "version": "11b08f2712264bbed731545085cbd9702096ceb7" + }, "zlib": { "hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", "min_version": "1.2", @@ -346,13 +351,13 @@ }, "zstd": { "find_args": "MODULE", - "hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029", + "hash": "2b006c3c110c38bd2f5896b42d36a38931c8eae261f30552f9a9fd04458d218d8f1a1425f117fd12af545ed77042e4b2d1a1a9b6286c4d0b9d74ef79f20a8f08", "min_version": "1.5", "options": [ "ZSTD_BUILD_SHARED OFF" ], "repo": "facebook/zstd", "source_subdir": "build/cmake", - "version": "b8d6101fba" + "version": "33618c8" } } diff --git a/dist/languages/ar.ts b/dist/languages/ar.ts index cd37ef9739..73ab8a2039 100644 --- a/dist/languages/ar.ts +++ b/dist/languages/ar.ts @@ -303,27 +303,27 @@ This would ban both their forum username and their IP address. Enable Overlay Applet - تمكين التطبيق المصغر للطبقة + تفعيل التطبيق المصغر للطبقة Enables Horizon's built-in overlay applet. Press and hold the home button for 1 second to show it. - تمكين التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره. + تفعيل التطبيق المصغر المدمج في هورايزون. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره. Output Engine: - محرك الإخراج: + :محرك الإخراج Output Device: - جهاز الإخراج: + :جهاز الإخراج Input Device: - جهاز الإدخال: + :جهاز الإدخال @@ -333,7 +333,7 @@ This would ban both their forum username and their IP address. Volume: - الصوت: + :الصوت @@ -413,7 +413,7 @@ Can help reduce stuttering at lower framerates. Accuracy: - الدقة: + :دقة المحاكاة @@ -424,12 +424,12 @@ Can help reduce stuttering at lower framerates. Backend: - الخلفية: + :الخلفية CPU Clocks - ترددات وحدة المعالجة المركزية + سرعات وحدة المعالجة المركزية @@ -451,7 +451,7 @@ Weaker CPUs may see reduced performance, and certain games may behave improperly Enable Host MMU Emulation (fastmem) - (fastmem) المضيف MMU تمكين محاكاة + (fastmem) المضيف MMU تفعيل محاكاة @@ -528,7 +528,7 @@ Please note this may result in deadlocks and other race conditions. API: - واجهة برمجة التطبيقات: + :واجهة برمجة التطبيقات @@ -540,7 +540,7 @@ Vulkan يُنصح باستخدام Device: - جهاز: + :جهاز @@ -550,7 +550,7 @@ Vulkan يُنصح باستخدام Resolution: - الدقة: + :الدقة @@ -564,7 +564,7 @@ Options lower than 1X can cause artifacts. Window Adapting Filter: - مرشح تكييف النافذة: + :مرشح التكيف مع النافذة @@ -579,7 +579,7 @@ Options lower than 1X can cause artifacts. Anti-Aliasing Method: - طريقة مضاد التعرج: + :طريقة مضاد التعرج @@ -591,12 +591,22 @@ FXAA can produce a more stable picture in lower resolutions. يُمكن لـ FXAA إنتاج صورة أكثر ثباتًا بدقة أقل. - - Fullscreen Mode: - وضع ملء الشاشة: + + Enable post-processing effects + تفعيل تأثيرات المعالجة اللاحقة - + + Applies post-processing effects to the final image. + يُطبق تأثيرات ما بعد المعالجة على الصورة النهائية. + + + + Fullscreen Mode: + :وضع ملء الشاشة + + + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -605,12 +615,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp قد يوفر وضع ملء الشاشة الحصري أداءً أفضل ودعمًا أفضل لتقنيتي Freesync/Gsync. - + Aspect Ratio: - نسبة العرض إلى الارتفاع: + :نسبة الأبعاد - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -619,48 +629,48 @@ Also controls the aspect ratio of captured screenshots. كما يتحكم في نسبة العرض إلى الارتفاع للقطات الشاشة الملتقطة. - + Use persistent pipeline cache استخدام ذاكرة التخزين المؤقتة الدائمة للأنابيب - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. يسمح بحفظ التظليلات في وحدة التخزين لتحميلها بشكل أسرع عند تشغيل اللعبة في المرات التالية. لا يُقصد من تعطيله سوى تصحيح الأخطاء. - + Use asynchronous GPU emulation استخدم محاكاة وحدة معالجة الرسومات غير المتزامنة - + Uses an extra CPU thread for rendering. This option should always remain enabled. يستخدم خيط معالجة إضافي للمعالجة الرسومية. يجب أن يبقى هذا الخيار مُفعّلاً دائماً. - + NVDEC emulation: NVDEC محاكاة: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. يُحدد كيفية فك تشفير الفيديوهات.يمكن استخدام وحدة المعالجة المركزية CPU أو وحدة معالجة الرسومات GPU لفك التشفير، أو عدم فك التشفير إطلاقًا (شاشة سوداء على الفيديوهات).في معظم الحالات، يُوفر فك تشفير وحدة معالجة الرسومات GPU أفضل أداء. - + ASTC Decoding Method: ASTC طريقة فك تشفير: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -672,12 +682,12 @@ stuttering but may present artifacts. وحدة المعالجة المركزية بشكل غير متزامن: استخدمها لفك تشفير قوام ASTC عند الطلب. يزيل هذا الخيار تقطع فك تشفير ASTC، ولكنه قد يُظهر بعض العيوب. - + ASTC Recompression Method: ASTC طريقة إعادة ضغط: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -685,44 +695,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: سيتم إعادة ضغط الصيغة الوسيطة إلى صيغة BC1 أو BC3، مما يوفر مساحة على ذاكرة الوصول العشوائي للفيديو VRAM ولكنه يُضعف جودة الصورة. - + Frame Pacing Mode (Vulkan only) (Vulkan فقط) وضع توقيت الإطارات - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. يتحكم في كيفية إدارة المحاكي لسرعة الإطارات لتقليل التقطع وجعل معدل الإطارات أكثر سلاسة واتساقًا. - + VRAM Usage Mode: VRAM وضع استهلاك: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. يُحدد ما إذا كان ينبغي على المُحاكي تفضيل توفير الذاكرة أو الاستفادة القصوى من ذاكرة الفيديو المُتاحة لتحسين الأداء. قد يؤثر الوضع المُكثّف على أداء تطبيقات أخرى، مثل برامج التسجيل. - + Skip CPU Inner Invalidation تخطي إبطال وحدة المعالجة المركزية الداخلية - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقتة أثناء تحديثات الذاكرة، مما يقلل من استخدام وحدة المعالجة المركزية ويحسّن زمن الوصول. قد يؤدي هذا إلى أعطال مؤقتة. - + + GPU Readback Before NCE Invalidation + NCE إعادة قراءة وحدة معالجة الرسومات قبل إبطال صلاحية + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + يفرغ بيانات ذاكرة المعالج الرسومي المؤقتة ويعيدها إلى ذاكرة المحاكي قبل أن تلغي تقنية التشغيل المباشر عمل الصفحة بالكامل، مما يحافظ على البيانات أثناء استمرار المعالج المركزي في تخزينها + + + + Patch Runtime NROs for NCE + تعديل ملفات تشغيل اللعبة فوراً لتتوافق مع تقنية التشغيل المباشر + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + يعدل كود تشغيل اللعبة تلقائياً عند فتحها. هذا الإجراء ضروري لتشغيل بعض الألعاب بدون مشاكل. + + + VSync Mode: VSync وضع: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -733,12 +763,12 @@ Immediate (no synchronization) presents whatever is available and can exhibit te يُظهر الوضع الفوري (بدون مزامنة) كل ما هو متاح، وقد يُظهر تمزقًا. - + Sync Memory Operations مزامنة عمليات الذاكرة - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -747,44 +777,44 @@ Unreal Engine 4 games often see the most significant changes thereof. التغييرات الأكثر أهمية Unreal Engine 4 غالبًا ما تشهد ألعاب. - + Enable asynchronous presentation (Vulkan only) - (Vulkan فقط) تمكين العرض التقديمي غير المتزامن + (Vulkan فقط) تفعيل العرض التقديمي غير المتزامن - + Slightly improves performance by moving presentation to a separate CPU thread. تحسين الأداء بشكل طفيف عن طريق نقل العرض التقديمي إلى مؤشر ترابط منفصل في وحدة المعالجة المركزية. - + Force maximum clocks (Vulkan only) (Vulkan فقط) فرض الحد الأقصى للساعات - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. يعمل في الخلفية أثناء انتظار أوامر الرسومات لمنع وحدة معالجة الرسومات من خفض سرعة الساعة. - + Anisotropic Filtering: - الترشيح المتباين الخواص: + :الترشيح متباين الخواص - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. يتحكم بجودة عرض الملمس بزوايا مائلة. من الآمن ضبطه على 16x على معظم وحدات معالجة الرسومات. - + GPU Mode: - وضع وحدة معالجة الرسومات: + :وضع وحدة معالجة الرسومات - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -793,12 +823,12 @@ Particles tend to only render correctly with Accurate mode. لا تُعرض الجسيمات بشكل صحيح إلا في وضع «دقيق». - + DMA Accuracy: DMA دقة: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -807,76 +837,62 @@ Default follows the GPU Accuracy setting. يُتبع الإعداد الافتراضي إعداد «دقة وحدة معالجة الرسومات» - + GPU Fence Behavior: :سلوك حاجز وحدة معالجة الرسومات - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. - يتحكم في سلوك تزامن حاجز وحدة معالجة الرسوميات. -الخيار الفوري هو الأسرع، لكنه قد يسبب بعض المشاكل. -الوضع المتوازن يوفر توافقًا أفضل وقد يحل بعض المشاكل في بعض الألعاب. -الدقة تحسّن التوافق أكثر مقابل بعض الأداء. -الوضع الصارم هو الخيار الأبطأ، لكنه يمكن أن يصلح المشاكل التي تتطلب مزامنة أكثر صرامة. -الإعداد الافتراضي يتبع إعداد دقة وحدة معالجة الرسومات. +Default follows the GPU Mode setting. + يتحكم في سلوك مزامنة حاجز وحدة معالجة الرسومات. +يُعد الخيار فوري الأسرع، لكنه قد يتسبب في بعض المشكلات. +يوفر الخيار متوازن توافقًا أفضل وقد يعمل على حل المشكلات في بعض الألعاب. +يعمل الخيار دقيق على تحسين التوافق بشكل أكبر على حساب بعض الأداء. +يتبع الخيار افتراضي إعداد وضع وحدة معالجة الرسومات. - - Enable GPU buffer readback - تفعيل قراءة مخزن وحدة معالجة الرسومات - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - يحافظ على البيانات المعدلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها قبل رفعها. -بعض الألعاب تحتاج هذا لعرض تأثيرات معينة بشكل صحيح. - - - + Enable asynchronous shader compilation تفعيل تجميع التظليل غير المتزامن - + May reduce shader stutter. قد يقلل من تقطع التظليل. - + GPU Clocks - سرعات معالج الرسوميات + سرعات وحدة معالجة الرسومات - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. يجعل اللعبة تعتقد أن عمل وحدة معالجة الرسومات ينتهي بشكل أسرع مما هو عليه في الواقع، لذلك تتوقف عن خفض الدقة ومسافة العرض لتتناسب مع سرعات جهاز Switch. - + GPU Unswizzle إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. باستخدام حوسبة وحدة معالجة الرسومات BCn 3D يسرع فك تشفير نسيج. قم بتعطيله في حالة حدوث أعطال أو مشاكل في الرسومات. - + GPU Unswizzle Max Texture Size الحد الأقصى لحجم النسيج في وحدة معالجة الرسومات بعد إعادة ترتيب البيانات - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -885,48 +901,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size حجم تدفق إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. يحدد الحد الأقصى لكمية بيانات النسيج (بالميغابايت) التي تتم معالجتها لكل إطار. يمكن أن تقلل القيم الأعلى من التقطع أثناء تحميل النسيج، ولكنها قد تؤثر على اتساق الإطارات. - + GPU Unswizzle Chunk Size حجم كتلة إلغاء ترتيب بيانات وحدة معالجة الرسومات - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. يُحدد هذا الخيار عدد شرائح العمق التي تتم معالجتها في عملية إرسال واحدة. زيادة هذا الخيار قد تُحسّن الإنتاجية على وحدات معالجة الرسومات المتطورة، ولكنها قد تُسبب أخطاء في إعادة تعيين وقت الاستجابة أو انقطاع الاتصال ببرنامج التشغيل على الأجهزة ذات المواصفات الأقل. - + Use Vulkan pipeline cache Vulkan استخدام ذاكرة التخزين المؤقتة لخط أنابيب - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. يُفعّل ذاكرة التخزين المؤقت لخطوط الأنابيب الخاصة ببائع وحدة معالجة الرسومات. بتخزين ملفات ذاكرة التخزين المؤقتة للخطوط الداخلية Vulkan يمكن أن يؤدي هذا الخيار إلى تحسين وقت تحميل التظليل بشكل كبير في الحالات التي لا يقوم فيها برنامج تشغيل. - + Enable Compute Pipelines (Intel Vulkan Only) - (Intel Vulkan فقط) تمكين خطوط أنابيب الحوسبة + (Intel Vulkan فقط) تفعيل خطوط أنابيب الحوسبة - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -935,192 +951,180 @@ Compute pipelines are always enabled on all other drivers. خطوط أنابيب الحوسبة مفعلة دائمًا على جميع برامج التشغيل الأخرى. - + Enable Reactive Flushing - تمكين التنظيف التفاعلي + تفعيل التنظيف التفاعلي - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. يستخدم التنظيف التفاعلي بدلاً من التنظيف التنبئي، مما يسمح بمزامنة الذاكرة بشكل أكثر دقة. - + Sync to framerate of video playback المزامنة مع معدل الإطارات لتشغيل الفيديو - + Run the game at normal speed during video playback, even when the framerate is unlocked. قم بتشغيل اللعبة بالسرعة العادية أثناء تشغيل الفيديو، حتى عندما يكون معدل الإطارات مفتوحًا. - + Barrier feedback loops حلقات التغذية الراجعة للحواجز - + Improves rendering of transparency effects in specific games. يحسن عرض تأثيرات الشفافية في بعض الألعاب المحددة. - + Enable buffer history - تمكين سجل التخزين المؤقت + تفعيل سجل التخزين المؤقت - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب. - + Fix bloom effects إصلاح تأثيرات التوهج - + Removes bloom in Burnout. يزيل التوهج في وضع الاحتراق. - - Enable Legacy Rescale Pass - تفعيل ميزة إعادة التحجيم القديمة - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - قد يصلح مشاكل إعادة القياس في بعض الألعاب بالاعتماد على سلوك من التنفيذ السابق. -حل لسلوك قديم يصلح تشوهات الخطوط على بطاقات AMD وIntel الرسومية، ووميض النسيج الرمادي على بطاقات Nvidia الرسومية في لعبة Luigi's Mansion 3. - - - + Extended Dynamic State الحالة الديناميكية الموسعة - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. يتحكم في عدد الميزات التي يمكن استخدامها في الحالة الديناميكية الموسعة.تسمح الحالات الأعلى بمزيد من الميزات ويمكن أن تزيد من الأداء، ولكنها قد تسبب مشكلات رسومية إضافية. - + Vertex Input Dynamic State حالة ديناميكية لإدخال الرأس - + Enables vertex input dynamic state feature for better quality and performance. يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء. - + Sample Shading تظليل العينة - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. يسمح لمظلل الأجزاء بالتنفيذ لكل عينة في جزء متعدد العينات بدلاً من مرة واحدة لكل جزء. يحسن جودة الرسومات على حساب الأداء. القيم الأعلى تحسن الجودة ولكنها تقلل من الأداء. - + RNG Seed مولد الأرقام العشوائية - + Controls the seed of the random number generator. Mainly used for speedrunning. يتحكم في بذرة مولد الأرقام العشوائية. يُستخدم بشكل رئيسي في سباقات السرعة. - + Device Name اسم الجهاز - + The name of the console. اسم وحدة التحكم. - + Homebrew Args Homebrew وسائط - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). (noglsl -مثل) عند التشغيل Homebrew سطر الأوامر الوسائط المُمررة إلى - + Custom RTC Date: - المخصص RTC تاريخ: + :تاريخ التوقيت الحقيقي المخصص - + This option allows to change the clock of the console. Can be used to manipulate time in games. يتيح لك هذا الخيار تغيير ساعة وحدة التحكم. يمكن استخدامه للتحكم بالوقت في الألعاب. - + The number of seconds from the current unix time عدد الثواني من وقت يونكس الحالي - + Language: - اللغة: + :اللغة - + This option can be overridden when region setting is auto-select يمكن تجاوز هذا الخيار عند تحديد إعداد المنطقة تلقائيًا - + Region: - المنطقة: + :المنطقة - + The region of the console. منطقة وحدة التحكم. - + Time Zone: - المنطقة الزمنية: + :المنطقة الزمنية - + The time zone of the console. المنطقة الزمنية لوحدة التحكم. - + Sound Output Mode: - وضع إخراج الصوت: + :وضع إخراج الصوت - + Console Mode: - وضع وحدة التحكم: + :وضع وحدة التحكم - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1129,59 +1133,47 @@ Setting to Handheld can help improve performance for low end systems. يُمكن أن يُساعد الضبط على الوضع المحمول على تحسين أداء الأجهزة منخفضة المواصفات. - + Prompt for user profile on boot المطالبة بملف تعريف المستخدم عند التشغيل - + Useful if multiple people use the same PC. مفيد إذا كان هناك عدة أشخاص يستخدمون نفس الكمبيوتر. - + Pause when not in focus توقف عند عدم التركيز - + Pauses emulation when focusing on other windows. يوقف المحاكاة عند التركيز على نوافذ أخرى. - + Confirm before stopping emulation تأكيد قبل إيقاف المحاكاة - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. يتجاوز المطالبات التي تطلب تأكيد إيقاف المحاكاة. -يؤدي تمكينه إلى تجاوز هذه المطالبات والخروج مباشرة من المحاكاة. +يؤدي تفعيله إلى تجاوز هذه المطالبات والخروج مباشرة من المحاكاة. - + Hide mouse on inactivity إخفاء الماوس عند عدم النشاط - + Hides the mouse after 2.5s of inactivity. يخفي الماوس بعد 2.5 ثانية من عدم النشاط. - - - Disable controller applet - تعطيل تطبيق التحكم - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - يُعطّل استخدام أداة التحكم في البرامج المُحاكاة قسرًا. -عند محاولة أي برنامج فتح أداة التحكم، تُغلق فورًا. - Check for updates @@ -1195,7 +1187,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Gamemode - تمكين وضع اللعبة + تفعيل وضع اللعبة @@ -1213,155 +1205,156 @@ When a program attempts to open the controller applet, it is immediately closed. تطبيق صغير حقيقي - + + + Disabled + معطل + + + Never أبداً - + On Load عند التحميل - + Always دائماً - + CPU وحدة المعالجة المركزية - + GPU وحدة معالجة الرسومات - + CPU Asynchronous وحدة المعالجة المركزية غير المتزامنة - + Uncompressed (Best quality) Uncompressed (أفضل جودة) - + BC1 (Low quality) BC1 (جودة منخفضة) - + BC3 (Medium quality) BC3 (جودة متوسطة) - + Auto تلقائي - + 30 FPS 30 إطارًا في الثانية - + 60 FPS 60 إطارًا في الثانية - + 90 FPS 90 إطارًا في الثانية - + 120 FPS 120 إطارًا في الثانية - + Conservative محافظ - + Aggressive عدواني - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null لا شيء - + Fast سريع - - + + Accurate دقه - - + + Default افتراضي - + Unsafe (fast) غير آمن (سريع) - + Safe (stable) آمن (مستقر) - + Immediate فوري - + Balanced متوازن - - - Strict - صارم - Unsafe @@ -1370,7 +1363,7 @@ When a program attempts to open the controller applet, it is immediately closed. Paranoid (disables most optimizations) - جنون العظمة (يعطل معظم التحسينات) + شديد الحذر (يعطل معظم التحسينات) @@ -1680,7 +1673,7 @@ When a program attempts to open the controller applet, it is immediately closed. British English - الإنكليزية البريطانية + الإنجليزية البريطانية @@ -1958,7 +1951,7 @@ When a program attempts to open the controller applet, it is immediately closed. Universal - عالمي + Universal @@ -2034,7 +2027,7 @@ When a program attempts to open the controller applet, it is immediately closed. Normal - طبيعي + قياسي @@ -2138,11 +2131,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) عالي (512) - - - Disabled - معطل - ExtendedDynamicState 1 @@ -2216,7 +2204,7 @@ When a program attempts to open the controller applet, it is immediately closed. Input device: - :جهاز الادخال + :جهاز الإدخال @@ -2302,7 +2290,7 @@ When a program attempts to open the controller applet, it is immediately closed. <html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html> - <html><head/><body><p><span style=" font-weight:600;">للتصحيح فقط.</span><br/>إذا لم تكن متأكدًا من وظيفة هذه الخيارات، فاتركها جميعًا ممكّنة. <br/>عند تعطيل هذه الإعدادات، لا تصبح سارية المفعول إلا عند تمكين تصحيح أخطاء وحدة المعالجة المركزية. </p></body></html> + <html><head/><body><p><span style=" font-weight:600;">للتصحيح فقط.</span><br/>إذا لم تكن متأكدًا من وظيفة هذه الخيارات، فاتركها جميعًا ممكّنة. <br/>عند تعطيل هذه الإعدادات، لا تصبح سارية المفعول إلا عند تفعيل تصحيح أخطاء وحدة المعالجة المركزية. </p></body></html> @@ -2313,14 +2301,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">يعمل هذا التحسين على تسريع عمليات الوصول إلى الذاكرة بواسطة برنامج الضيف.</div> -<div style="white-space: nowrap">يؤدي تمكينه إلى تضمين الوصول إلى مؤشرات PageTable::pointers في الكود الصادر.</div> +<div style="white-space: nowrap">يؤدي تفعيله إلى تضمين الوصول إلى مؤشرات PageTable::pointers في الكود الصادر.</div> <div style="white-space: nowrap">يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على المرور عبر وظائف Memory::Read/Memory::Write.</div> Enable inline page tables - تمكين جداول الصفحات المضمنة + تفعيل جداول الصفحات المضمنة @@ -2334,7 +2322,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable block linking - تمكين ربط الكتلة + تفعيل ربط الكتلة @@ -2348,7 +2336,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable return stack buffer - تمكين مخزن مؤقت لمكدس الإرجاع + تفعيل مخزن مؤقت لمكدس الإرجاع @@ -2362,7 +2350,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable fast dispatcher - تمكين المرسل السريع + تفعيل الموزع السريع @@ -2376,7 +2364,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable context elimination - تمكين حذف السياق + تفعيل حذف السياق @@ -2390,7 +2378,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable constant propagation - تمكين الانتشار الثابت + تفعيل الانتشار الثابت @@ -2404,7 +2392,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable miscellaneous optimizations - تمكين التحسينات المتنوعة + تفعيل التحسينات المتنوعة @@ -2413,14 +2401,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> - <div style="white-space: nowrap">عند التمكين، لا يتم تشغيل عدم المحاذاة إلا عندما يتجاوز الوصول حدود الصفحة</div> + <div style="white-space: nowrap">عند التفعيل، لا يتم تشغيل عدم المحاذاة إلا عندما يتجاوز الوصول حدود الصفحة</div> <div style="white-space: nowrap">عند التعطيل، يتم تشغيل عدم المحاذاة على جميع عمليات الوصول غير المتوافقة</div> Enable misalignment check reduction - تمكين تقليل التحقق من المحاذاة غير الصحيحة + تفعيل تقليل التحقق من المحاذاة غير الصحيحة @@ -2431,14 +2419,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">يعمل هذا التحسين على تسريع عمليات الوصول إلى الذاكرة بواسطة برنامج الضيف.</div> -<div style="white-space: nowrap">يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من وحدة MMU الخاصة بالمضيف.</div> +<div style="white-space: nowrap">يؤدي تفعيله إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من وحدة MMU الخاصة بالمضيف.</div> <div style="white-space: nowrap">يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</div> Enable Host MMU Emulation (general memory instructions) - المضيف (تعليمات الذاكرة العامة) MMU تمكين محاكاة + المضيف (تعليمات الذاكرة العامة) MMU تفعيل محاكاة @@ -2449,14 +2437,14 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">يعمل هذا التحسين على تسريع عمليات الوصول الحصري للذاكرة بواسطة برنامج الضيف.</div> -<div style="white-space: nowrap">يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة للذاكرة الحصرية للضيوف مباشرة في الذاكرة والاستفادة من وحدة MMU الخاصة بالمضيف.</div> +<div style="white-space: nowrap">يؤدي تفعيله إلى إجراء عمليات قراءة/كتابة للذاكرة الحصرية للضيوف مباشرة في الذاكرة والاستفادة من وحدة MMU الخاصة بالمضيف.</div> <div style="white-space: nowrap">يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول الحصرية للذاكرة على استخدام محاكاة MMU البرمجية.</div> Enable Host MMU Emulation (exclusive memory instructions) - المضيف (تعليمات الذاكرة الحصرية) MMU تمكين محاكاة + المضيف (تعليمات الذاكرة الحصرية) MMU تفعيل محاكاة @@ -2466,13 +2454,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">يعمل هذا التحسين على تسريع عمليات الوصول الحصري للذاكرة بواسطة برنامج الضيف.</div> -<div style="white-space: nowrap">يؤدي تمكينه إلى تقليل التكلفة الإضافية لفشل fastmem في الوصول إلى الذاكرة الحصرية.</div> +<div style="white-space: nowrap">يؤدي تفعيله إلى تقليل التكلفة الإضافية لفشل fastmem في الوصول إلى الذاكرة الحصرية.</div> Enable recompilation of exclusive memory instructions - تمكين إعادة تجميع تعليمات الذاكرة الحصرية + تفعيل إعادة تجميع تعليمات الذاكرة الحصرية @@ -2482,13 +2470,13 @@ When a program attempts to open the controller applet, it is immediately closed. <div style="white-space: nowrap">يعمل هذا التحسين على تسريع عمليات الوصول إلى الذاكرة عن طريق السماح بنجاح الوصول إلى الذاكرة غير الصالحة.</div> - <div style="white-space: nowrap">يؤدي تمكينه إلى تقليل الحمل الزائد لجميع عمليات الوصول إلى الذاكرة وليس له أي تأثير على البرامج التي لا تصل إلى الذاكرة غير الصالحة.</div> + <div style="white-space: nowrap">يؤدي تفعيله إلى تقليل الحمل الزائد لجميع عمليات الوصول إلى الذاكرة وليس له أي تأثير على البرامج التي لا تصل إلى الذاكرة غير الصالحة.</div> Enable fallbacks for invalid memory accesses - تمكين الإجراءات الاحتياطية للوصول إلى الذاكرة غير الصالحة + تفعيل الإجراءات الاحتياطية للوصول إلى الذاكرة غير الصالحة @@ -2506,7 +2494,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable GDB Stub - GDB Stub تمكين + GDB Stub تفعيل @@ -2616,12 +2604,12 @@ When a program attempts to open the controller applet, it is immediately closed. When checked, it enables Nsight Aftermath crash dumps - Nsight Aftermath عند تحديد هذا الخيار، يتم تمكين عمليات تفريغ الأعطال الخاصة بـ + Nsight Aftermath عند تحديد هذا الخيار، يتم تفعيل عمليات تفريغ الأعطال الخاصة بـ Enable Nsight Aftermath - Nsight Aftermath تمكين + Nsight Aftermath تفعيل @@ -2636,7 +2624,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Renderdoc Hotkey - Renderdoc تمكين مفتاح التشغيل السريع لـ + Renderdoc تفعيل مفتاح التشغيل السريع لـ @@ -2666,7 +2654,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Graphics Debugging - تمكين تصحيح الرسومات + تفعيل تصحيح الرسومات @@ -2676,7 +2664,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Shader Feedback - تمكين ملاحظات التظليل + تفعيل ملاحظات التظليل @@ -2721,12 +2709,12 @@ When a program attempts to open the controller applet, it is immediately closed. Enable All Controller Types - تمكين كافة أنواع اذرع التحكم + تفعيل كافة أنواع اذرع التحكم Enable Auto-Stub - تمكين الملحق تلقائي + تفعيل الملحق تلقائي @@ -2741,7 +2729,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable Debug Asserts - تمكين تأكيدات التصحيح + تفعيل تأكيدات التصحيح @@ -2751,12 +2739,12 @@ When a program attempts to open the controller applet, it is immediately closed. Battery Serial: - :رقم تسلسلي للبطارية + :الرقم التسلسلي للبطارية Debug Knobs: - أزرار تصحيح الأخطاء: + :أزرار تصحيح الأخطاء @@ -2786,7 +2774,7 @@ When a program attempts to open the controller applet, it is immediately closed. Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer. - قم بتمكين هذه الخيار لإخراج أحدث قائمة أوامر صوتية تم إنشاؤها إلى وحدة التحكم. يؤثر فقط على الألعاب التي تستخدم عارض الصوت. + قم بتفعيل هذه الخيار لإخراج أحدث قائمة أوامر صوتية تم إنشاؤها إلى وحدة التحكم. يؤثر فقط على الألعاب التي تستخدم عارض الصوت. @@ -2801,12 +2789,12 @@ When a program attempts to open the controller applet, it is immediately closed. Enable FS Access Log - FS تمكين سجل وصول + FS تفعيل سجل وصول Enable Verbose Reporting Services** - تمكين خدمات التقارير التفصيلية + تفعيل خدمات التقارير التفصيلية @@ -3096,7 +3084,7 @@ When a program attempts to open the controller applet, it is immediately closed. Choose an action for the save data directory: - اختر إجراءً لمجلد بيانات الحفظ: + :اختر إجراءً لمجلد بيانات الحفظ @@ -3264,33 +3252,38 @@ Would you like to delete the old save data? :لون الخلفية - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + إعداد التأثيرات... + + + Off معطل - + VSync Off VSync معطل - + Recommended مستحسن - + On مفعل - + VSync On VSync مفعل @@ -3410,7 +3403,7 @@ Would you like to delete the old save data? [waiting] - [انتظار] + [جارٍ الانتظار] @@ -3747,7 +3740,7 @@ Would you like to delete the old save data? Ring Controller - وحدة تحكم الحلقة + ذراع تحكم الحلقة @@ -3774,12 +3767,12 @@ Would you like to delete the old save data? Enable XInput 8 player support (disables web applet) - (يعطل تطبيق الويب الصغير) XInput 8 تمكين دعم مشغل + (يعطل تطبيق الويب الصغير) XInput 8 تفعيل دعم مشغل Enable UDP controllers (not needed for motion) - (غير مطلوبة للحركة) UDP تمكين وحدات التحكم + (غير مطلوبة للحركة) UDP تفعيل وحدات التحكم @@ -3789,12 +3782,12 @@ Would you like to delete the old save data? Enable direct JoyCon driver - تمكين برنامج تشغيل جوي كون المباشر + تفعيل برنامج تشغيل جوي كون المباشر Enable direct Pro Controller driver [EXPERIMENTAL] - المباشر [تجريبي] Pro Controller تمكين برنامج تشغيل + المباشر [تجريبي] Pro Controller تفعيل برنامج تشغيل @@ -4327,7 +4320,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< [waiting] - [انتظار] + [جارٍ الانتظار] @@ -4337,7 +4330,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Enter a profile name: - أدخل اسم ملف التعريف: + :أدخل اسم ملف التعريف @@ -4517,7 +4510,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Testing - اختبار + جارٍ الاختبار @@ -4560,7 +4553,7 @@ To invert the axes, first move your joystick vertically, and then horizontally.< Enable mouse panning - تمكين تحريك الماوس + تفعيل تحريك الماوس @@ -4636,12 +4629,12 @@ Current values are %1% and %2% respectively. Emulated mouse is enabled. This is incompatible with mouse panning. - تم تمكين الماوس الذي تمت محاكاته. وهذا غير متوافق مع تحريك الماوس. + تم تفعيل الماوس الذي تمت محاكاته. وهذا غير متوافق مع تحريك الماوس. Emulated mouse is enabled - تم تمكين الماوس الذي تمت محاكاته + تم تفعيل الماوس الذي تمت محاكاته @@ -4674,7 +4667,7 @@ Current values are %1% and %2% respectively. Enable Airplane Mode - تمكين وضع الطيران + تفعيل وضع الطيران @@ -4917,103 +4910,118 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. + تُطبق التغييرات فورًا أثناء سير اللعبة. - + Enable post-processing تفعيل المعالجة اللاحقة - + Preset: - الإعداد المسبق: + :الإعداد المسبق - + Apply تطبيق - + Reset Values إعادة تعيين القيم - + Return every effect to the values the preset ships with. إعادة كل تأثير إلى القيم التي تأتي بها الإعدادات المسبقة. - + Save As... حفظ باسم... - + Save Preset حفظ الإعداد المسبق - + Preset name: - اسم الإعداد المسبق: + :اسم الإعداد المسبق - - Delete - حذف + + Clear + مسح - + + Remove every effect in use and deselect the preset. + قم بإزالة جميع المؤثرات قيد الاستخدام وإلغاء تحديد الإعداد المسبق. + + + Add Effect إضافة تأثير - + + None + لا شيء + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + لا توجد تأثيرات قيد الاستخدام. اختر إعدادًا مسبقًا واضغط على تطبيق، أو أضف التأثيرات واحدًا تلو الآخر. + + + Custom chain. Pick a preset above and press Apply to replace it. سلسلة مخصصة. اختر إعدادًا مسبقًا أعلاه واضغط على تطبيق لاستبداله. - + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - الإعداد المسبق النشط: 1% (معدّل). اضغط على إعادة تعيين القيم للعودة إلى الإعدادات الافتراضية. + الإعداد المسبق النشط: %1 (معدّل). اضغط على إعادة تعيين القيم للعودة إلى الإعدادات الافتراضية. - + Active preset: %1 - الإعداد المسبق النشط: 1% + الإعداد المسبق النشط: %1 - + Enabled تفعيل - + Disabled تعطيل - + Reset to defaults إعادة التعيين إلى الإعدادات الافتراضية - + Effect '%1' was not found. لم يتم العثور على التأثير '1%'. - + Effect failed to compile: %1 فشل تجميع التأثير: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -5096,7 +5104,7 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t Unable to create directory %1 for storing user images. - غير قادر على إنشاء الدليل %1 لتخزين صور المستخدم. + تعذر إنشاء المجلد %1 لتخزين صور المستخدم. @@ -5149,7 +5157,7 @@ UUID: %2 Configure Ring Controller - إعدادات حلقة وحدة التحكم + إعدادات حلقة ذراع التحكم @@ -5186,7 +5194,7 @@ UUID: %2 Enable Ring Input - تمكين إدخال الحلقة + تفعيل إدخال الحلقة @@ -5234,12 +5242,12 @@ UUID: %2 Error enabling ring input - حدث خطأ أثناء تمكين إدخال الحلقة + حدث خطأ أثناء تفعيل إدخال الحلقة Direct Joycon driver is not enabled - لم يتم تمكين برنامج تشغيل جوي كون المباشر + لم يتم تفعيل برنامج تشغيل جوي كون المباشر @@ -5249,12 +5257,12 @@ UUID: %2 The current mapped device doesn't support the ring controller - الجهاز المُعيَّن حاليًا لا يدعم وحدة التحكم بالحلقة + الجهاز المعين حاليا لا يدعم ذراع التحكم بالحلقة The current mapped device doesn't have a ring attached - الجهاز المُعَيَّن حاليًا ليس به حلقة مُرفقة + الجهاز المعين حاليا ليس به حلقة مرفقة @@ -5269,7 +5277,7 @@ UUID: %2 [waiting] - [انتظار] + [جارٍ الانتظار] @@ -5326,7 +5334,7 @@ UUID: %2 Enable TAS features - TAS تمكين ميزات + TAS تفعيل ميزات @@ -5661,7 +5669,7 @@ Drag points to change position, or double-click table cells to edit values. English - الإنجليزية + English @@ -5747,7 +5755,7 @@ Drag points to change position, or double-click table cells to edit values. Enable Accurate Vibration - تمكين الاهتزاز الدقيق + تفعيل الاهتزاز الدقيق @@ -5790,7 +5798,7 @@ Drag points to change position, or double-click table cells to edit values. Discord Presence - الحضور على ديسكورد + التواجد على ديسكورد @@ -5888,7 +5896,7 @@ Drag points to change position, or double-click table cells to edit values. Delete all data in this directory. THIS IS 100% IRREVERSABLE! - حذف جميع البيانات الموجودة في هذا الدليل. هذا الإجراء لا رجعة فيه بنسبة 100%! + حذف جميع البيانات الموجودة في هذا المجلد. هذه العملية لا رجعة فيها بنسبة 100%! @@ -5982,7 +5990,7 @@ Drag points to change position, or double-click table cells to edit values. Connecting - الاتصال + جارٍ الاتصال @@ -7031,7 +7039,7 @@ Debug Message: - + &Pause &إيقاف مؤقت @@ -7068,7 +7076,7 @@ Debug Message: Enable Overlay Display Applet - تمكين التطبيق المصغر لعرض الطبقة + تفعيل التطبيق المصغر لعرض الطبقة @@ -7182,7 +7190,7 @@ Debug Message: - + &Start &بدء @@ -7193,7 +7201,7 @@ Debug Message: - + R&ecord ت&سجيل @@ -7284,7 +7292,7 @@ Debug Message: - + None لا شيء @@ -7309,401 +7317,407 @@ Debug Message: &Carousel View &عرض دائري - - - Small (32x32) - صغير (32x32) - - - - Standard (64x64) - قياسي (64x64) - - Large (128x128) - كبير (128x128) + Small (32x32) + (32x32) صغير - Full Size (256x256) - حجم كامل (256x256) + Standard (64x64) + (64x64) قياسي - + + Large (128x128) + (128x128) كبير + + + + Full Size (256x256) + (256x256) حجم كامل + + + Broken Vulkan Installation Detected Vulkan تم الكشف عن تلف في تثبيت - + Vulkan initialization failed during boot. أثناء التشغيل Vulkan فشل تهيئة. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping تشغيل لعبة - + Loading Web Applet... جارٍ تحميل تطبيق الويب... - - + + Disable Web Applet تعطيل تطبيق الويب - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) قد يؤدي تعطيل أداة الويب إلى سلوك غير محدد، ويجب استخدامه فقط مع لعبة Super Mario 3D All-Stars. هل أنت متأكد من رغبتك في تعطيل أداة الويب؟ (يمكن إعادة تفعيلها في إعدادات التصحيح.) - + The amount of shaders currently being built كمية التظليلات التي يتم بناؤها حاليًا - + The current selected resolution scaling multiplier. مضاعف قياس الدقة المحددة حالياً. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Switch سرعة المحاكاة الحالية. تشير القيم الأعلى أو الأقل من 100٪ إلى أن المحاكاة تعمل بشكل أسرع أو أبطأ من. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. عدد الإطارات في الثانية التي تعرضها اللعبة حاليًا. يختلف هذا العدد من لعبة إلى أخرى ومن مشهد إلى آخر. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. الوقت المستغرق لمحاكاة إطار على جهاز سويتش، بدون احتساب تحديد الإطارات أو المزامنة العمودية. لمحاكاة بسرعة كاملة، يجب أن يكون هذا في حدود 16.67 مللي ثانية كحد أقصى. - + Unmute إلغاء كتم الصوت - + Mute كتم الصوت - + Reset Volume إعادة تعيين مستوى الصوت - + + Configure Effects... + إعداد التأثيرات... + + + &Clear Recent Files &مسح الملفات الحديثة - + &Continue &متابعة - + Warning: Outdated Game Format تحذير: تنسيق اللعبة قديم - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. أنت تستخدم تنسيق مجلد ROM المُفكك لهذه اللعبة، وهو تنسيق قديم استُبدل بآخر مثل NCA وNAX وXCI وNSP. تفتقر مجلدات ROM المُفككة إلى الأيقونات والبيانات الوصفية ودعم التحديثات. <br>لتوضيح تنسيقات Switch المختلفة التي يدعمها Eden، يُرجى مراجعة دليل المستخدم. لن تظهر هذه الرسالة مرة أخرى. - - + + Error while loading ROM! ROM خطأ أثناء تحميل - + The ROM format is not supported. غير مدعوم ROM تنسيق. - + An error occurred initializing the video core. حدث خطأ أثناء تهيئة نواة الفيديو. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - عادةً ما يحدث هذا بسبب برامج تشغيل بطاقة الرسوميات GPU القديمة. يرجى الاطلاع على ملف السجل لمزيد من التفاصيل. انظر: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'> كيفية الوصول إلى ملفات السجل</a>. + عادةً ما يحدث هذا بسبب برامج تشغيل وحدة معالجة الرسومات القديمة. يرجى الاطلاع على السجل لمزيد من التفاصيل. انظر: +<a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>كيفية الوصول إلى ملفات السجلات</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. حدث خطأ أثناء تحميل ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Discord/Stoat يرجى إعادة تحميل ملفاتك أو طلب المساعدة على - + An unknown error occurred. Please see the log for more details. حدث خطأ غير معروف. يرجى الاطلاع على السجل لمزيد من التفاصيل. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... جارٍ إغلاق البرنامج... - + Save Data بيانات الحفظ - + Mod Data بيانات التعديل - + Error Opening %1 Folder خطأ في فتح المجلد %1 - - + + Folder does not exist! المجلد غير موجود! - + Remove Installed Game Contents? إزالة محتويات اللعبة المثبتة؟ - + Remove Installed Game Update? إزالة تحديث اللعبة المثبت؟ - + Remove Installed Game DLC? إزالة المحتوى القابل للتنزيل المثبت للعبة؟ - + Remove Entry إزالة الإدخال - + Delete OpenGL Transferable Shader Cache? OpenGL Shader حذف ذاكرة التخزين المؤقتة القابلة للنقل لـ - + Delete Vulkan Transferable Shader Cache? Vulkan Shader حذف ذاكرة التخزين المؤقتة القابلة للنقل لـ - + Delete All Transferable Shader Caches? حذف جميع مخازن ذاكرة التظليل القابلة للنقل؟ - + Remove Custom Game Configuration? إزالة إعدادات اللعبة المخصص؟ - + Remove Cache Storage? إزالة ذاكرة التخزين المؤقتة؟ - + Remove File إزالة الملف - + Remove Play Time Data إزالة بيانات زمن اللعب - + Reset play time? إعادة تعيين زمن اللعب؟ - - + + RomFS Extraction Failed! RomFS فشل استخراج - + There was an error copying the RomFS files or the user cancelled the operation. أو قام المستخدم بإلغاء العملية RomFS حدث خطأ أثناء نسخ ملفات - + Full كامل - + Skeleton Skeleton - + Select RomFS Dump Mode RomFS حدد وضع تفريغ - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. الرجاء تحديد الطريقة التي تريد بها تفريغ RomFS. <br>سيتم نسخ جميع الملفات إلى المجلد الجديد في أثناء <br>قيام الهيكل العظمي بإنشاء بنية المجلد فقط. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root لا توجد مساحة فارغة كافية في %1 لاستخراج نظام الملفات RomFS. يُرجى تحرير مساحة أو اختيار مجلد تفريغ آخر من: المحاكاة > التكوين > النظام > نظام الملفات > تفريغ الجذر. - + Extracting RomFS... RomFS استخراج... - - + + Cancel إلغاء - + RomFS Extraction Succeeded! بنجاح RomFS تم استخراج! - + The operation completed successfully. تمت العملية بنجاح. - + Error Opening %1 خطأ في فتح %1 - + Select Directory حدد المجلد - + Properties خصائص - + The game properties could not be loaded. تعذر تحميل خصائص اللعبة. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. تشغيل الملف القابل للتنفيذ (%1)؛؛جميع الملفات (*.*) - + Load File تحميل الملف - + Open Extracted ROM Directory المستخرج ROM فتح ملف - + Invalid Directory Selected تم تحديد مجلد غير صالح - + The directory you have selected does not contain a 'main' file. لا يحتوي المجلد الذي حددته على ملف رئيسي - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files تثبيت الملفات - + %n file(s) remaining %n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية%n ملف (ملفات) متبقية - + Installing file "%1"... تثبيت الملف ”%1“... + - Install Results نتائج التثبيت - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. لتجنب أي تعارضات محتملة، ننصح المستخدمين بعدم تثبيت الألعاب الأساسية على الذاكرة الداخلية. يرجى استخدام هذه الميزة فقط لتثبيت التحديثات والمحتوى القابل للتنزيل. - + %n file(s) were newly installed %n تم تثبيت ملف (ملفات) جديدة @@ -7715,7 +7729,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n تم استبدال ملف (ملفات) @@ -7727,7 +7741,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n فشل تثبيت ملف (ملفات) @@ -7739,314 +7753,324 @@ Please, only use this feature to install updates and DLC. - + System Application تطبيق النظام - + System Archive أرشيف النظام - + System Application Update تحديث تطبيق النظام - + Firmware Package (Type A) حزمة الفيرموير (النوع أ) - + Firmware Package (Type B) حزمة الفيرموير (النوع ب) - + Game لعبة - + Game Update تحديث اللعبة - + Game DLC الخاص باللعبة DLC الـ - + Delta Title Delta عنوان - + Select NCA Install Type... NCA حدد نوع تثبيت... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) يرجى تحديد نوع اللعبة التي ترغب في تثبيت NCA عليها: (في معظم الحالات، يكون الإعداد الافتراضي "لعبة" مناسبًا.) - + Failed to Install فشل التثبيت - + The title type you selected for the NCA is invalid. غير صالح NCA نوع العنوان الذي حددته لـ. - + File not found لم يتم العثور على الملف - + File "%1" not found لم يتم العثور على الملف "%1" - + OK موافق - + Function Disabled الوظيفة معطلة - + Compatibility list reporting is currently disabled. Check back later! تقارير قائمة التوافق معطلة حاليًا. يرجى التحقق لاحقًا! - + Error opening URL خطأ في فتح الرابط - + Unable to open the URL "%1". تعذر فتح الرابط ”%1“ - + TAS Recording TAS تسجيل - + Overwrite file of player 1? الكتابة فوق ملف اللاعب 1؟ - + Invalid config detected تم اكتشاف إعدادات غير صالح - + Handheld controller can't be used on docked mode. Pro controller will be selected. لا يمكن استخدام وحدة التحكم المحمولة في وضع الإرساء. سيتم اختيار وحدة تحكم احترافية. - - + + Amiibo أميبو - - + + The current amiibo has been removed تمت إزالة أميبو الحالي. - + Error خطأ - - + + The current game is not looking for amiibos اللعبة الحالية لا تبحث عن أميبو - + Amiibo File (%1);; All Files (*.*) جميع الملفات (%1)؛؛ ملف أميبو (*.*) - + Load Amiibo تحميل أميبو - + Error loading Amiibo data خطأ أثناء تحميل بيانات أميبو - + The selected file is not a valid amiibo الملف المحدد ليس أميبو صالح - + The selected file is already on use الملف المحدد قيد الاستخدام بالفعل - + An unknown error occurred حدث خطأ غير معروف - + No firmware available لا يوجد فيرموير متوفر - + Firmware Corrupted الفيرموير تالف - + Unknown applet تطبيق غير معروف - + Applet doesn't map to a known value. لا يرتبط التطبيق المصغر بقيمة معروفة. - + Record not found لم يتم العثور على السجل - + Applet not found. Please reinstall firmware. لم يتم العثور على التطبيق المصغر. يرجى إعادة تثبيت الفيرموير. - + Capture Screenshot التقاط لقطة شاشة - + PNG Image (*.png) PNG Image (*.png) - + TAS state: Running %1/%2 حالة TAS: تشغيل %1/%2 - + TAS state: Recording %1 حالة TAS: تسجيل %1 - + TAS state: Idle %1/%2 حالة TAS: خامل %1/%2 - + TAS State: Invalid حالة TAS: غير صالحة - + &Stop Running &إيقاف التشغيل - + Stop R&ecording إيقاف ال&تسجيل - + Building: %n shader(s) بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات)بناء: %n تظليل(ات) - + Scale: %1x %1 is the resolution scaling factor الدقة: %1x - + Speed: %1% / %2% السرعة: %1% / %2% - + Speed: %1% السرعة: %1% - + Game: %1 FPS - اللعبة: %1 FPS + اللعبة: %1 إطار - + Frame: %1 ms الإطار: %1 ms - + NO AA NO AA - + + NO FX + NO FX + + + + FX + FX + + + VOLUME: MUTE الصوت: كتم الصوت - + VOLUME: %1% Volume percentage (e.g. 50%) %1% :الصوت - + Derivation Components Missing مكونات الاشتقاق مفقودة - + Decryption keys are missing. Install them now? مفاتيح فك التشفير مفقودة. هل تريد تثبيتها الآن؟ - + Wayland Detected! Wayland تم الكشف عن - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8057,74 +8081,74 @@ Would you like to force it for future launches? هل ترغب في فرض استخدامه في عمليات التشغيل المستقبلية؟ - + Use X11 X11 استخدم - + Continue with Wayland Wayland متابعة مع - + Don't show again لا تعرض مرة أخرى - + Restart Required إعادة التشغيل مطلوبة - + Restart Eden to apply the X11 backend. X11 أعد تشغيل إيدن لتطبيق الخلفية - + Slow بطيء - + Turbo تيربو - + Unlocked إلغاء القفل - + Select RomFS Dump Target RomFS حدد هدف تفريغ - + Please select which RomFS you would like to dump. الذي تريد تفريغه RomFS الرجاء تحديد - + Are you sure you want to close Eden? هل أنت متأكد من أنك تريد إغلاق إيدن؟ - - - + + + Eden إيدن - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. هل أنت متأكد من أنك تريد إيقاف المحاكاة؟ سيتم فقدان أي تقدم لم يتم حفظه. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8297,7 +8321,7 @@ If you wish to clean up the files which were left in the old data location, you %1 -إذا كنت ترغب في تنظيف الملفات التي تركت في موقع البيانات القديم، يمكنك القيام بذلك عن طريق حذف الدليل التالي: +إذا كنت ترغب في إزالة الملفات التي بقيت في موقع البيانات القديم، فيمكنك القيام بذلك عن طريق حذف المجلد التالي: %1 @@ -8643,7 +8667,7 @@ p, li { white-space: pre-wrap; } FPS - معدل الإطارات في الثانية + إطار في الثانية @@ -8653,7 +8677,7 @@ p, li { white-space: pre-wrap; } %1 fps - %1 معدل الإطارات في الثانية + %1 إطار في الثانية @@ -9249,7 +9273,7 @@ p, li { white-space: pre-wrap; } Creation Date - تاريخ الانشاء + تاريخ الإنشاء @@ -9294,7 +9318,7 @@ p, li { white-space: pre-wrap; } No game data present - لا توجد بيانات للعبة + لا توجد بيانات اللعبة @@ -9309,7 +9333,7 @@ p, li { white-space: pre-wrap; } Set nickname and owner: - تعيين الاسم المستعار والمالك + :تعيين الاسم المستعار والمالك @@ -9364,7 +9388,7 @@ p, li { white-space: pre-wrap; } Verification failed for the following files: %1 - فشل التحقق من الملفات التالية: + :فشل التحقق من الملفات التالية %1 @@ -9629,7 +9653,7 @@ OS reported error: %1 %1 To: %2 - تعذر ربط المجلد: + :تعذر ربط المجلد %1 To: %2 @@ -10528,7 +10552,7 @@ Please try again or contact the developer of the software. Select a user: - حدد مستخدمًا: + :حدد مستخدمًا diff --git a/dist/languages/ca.ts b/dist/languages/ca.ts index 6b9f560f4a..534d4e4af1 100644 --- a/dist/languages/ca.ts +++ b/dist/languages/ca.ts @@ -569,70 +569,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Mode pantalla completa: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Relació d'aspecte: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulació NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -641,55 +651,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Mode de sincronització vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -697,426 +727,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Filtrat anisotròpic: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: Mode de la GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Precisió DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Llavor de GNA - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nom del dispositiu - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Idioma: - + This option can be overridden when region setting is auto-select - + Region: Regió: - + The region of the console. - + Time Zone: Zona horària: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: Mode de la consola - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ocultar el cursor del ratolí en cas d'inactivitat - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1148,155 +1144,156 @@ When a program attempts to open the controller applet, it is immediately closed. - + + + Disabled + + + + Never Mai - + On Load - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservador - + Aggressive Agressiu - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Ensamblat d'ombrejadors, només NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, només AMD/Mesa) - + Null Nul - + Fast Ràpid - - + + Accurate Precís - - + + Default Valor predeterminat - + Unsafe (fast) Insegur (ràpid) - + Safe (stable) Segur (estable) - + Immediate - + Balanced Equilibrat - - - Strict - - Unsafe @@ -2073,11 +2070,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3198,33 +3190,38 @@ Would you like to delete the old save data? Color de fons: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Apagat - + VSync Off Vsync Apagat - + Recommended Recomanat - + On Encés - + VSync On VSync Encés @@ -4845,102 +4842,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6955,7 +6967,7 @@ Debug Message: - + &Pause &Pausar @@ -7106,7 +7118,7 @@ Debug Message: - + &Start &Iniciar @@ -7117,7 +7129,7 @@ Debug Message: - + R&ecord E&nregistrar @@ -7208,7 +7220,7 @@ Debug Message: - + None Cap @@ -7234,722 +7246,737 @@ Debug Message: - + Small (32x32) Petit (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Desilenciar - + Mute Silenciar - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full Complet - + Skeleton Esquelet - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Cancel·lar - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties Propietats - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File Carregar arxiu - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Instal·lar arxius - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game Joc - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK D'acord - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocitat: %1% / %2% - + Speed: %1% Velocitat: %1% - + Game: %1 FPS Joc: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - + NO AA SENSE AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUMEN: %1% - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7957,74 +7984,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow Lent - + Turbo Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/cs.ts b/dist/languages/cs.ts index 875ade6480..abbc0b4db0 100644 --- a/dist/languages/cs.ts +++ b/dist/languages/cs.ts @@ -569,70 +569,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Režim celé obrazovky: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Poměr stran: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -641,55 +651,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -697,426 +727,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Anizotropní filtrování: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Název Zařízení - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Jazyk: - + This option can be overridden when region setting is auto-select - + Region: Region: - + The region of the console. - + Time Zone: Časové Pásmo: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Potvrzení před zastavením emulace - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Skrýt myš při neaktivitě - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1148,155 +1144,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - + Auto Automatické - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - - + + Accurate Přesné - - + + Default Výchozí - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2073,11 +2070,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3190,33 +3182,38 @@ Would you like to delete the old save data? Barva Pozadí: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4837,102 +4834,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6945,7 +6957,7 @@ Debug Message: - + &Pause &Pauza @@ -7096,7 +7108,7 @@ Debug Message: - + &Start &Start @@ -7107,7 +7119,7 @@ Debug Message: - + R&ecord @@ -7198,7 +7210,7 @@ Debug Message: - + None @@ -7224,722 +7236,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7947,74 +7974,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/da.ts b/dist/languages/da.ts index 32ebbea332..4e34f94af9 100644 --- a/dist/languages/da.ts +++ b/dist/languages/da.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Fuldskærmstilstand: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Skærmformat: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Anisotropisk Filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG-Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Region - + The region of the console. - + Time Zone: Tidszone - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Skjul mus ved inaktivitet - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - + Auto Automatisk - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - - + + Accurate Nøjagtig - - + + Default Standard - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3198,33 +3190,38 @@ Would you like to delete the old save data? Baggrundsfarve: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4845,102 +4842,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6953,7 +6965,7 @@ Debug Message: - + &Pause @@ -7104,7 +7116,7 @@ Debug Message: - + &Start @@ -7115,7 +7127,7 @@ Debug Message: - + R&ecord @@ -7206,7 +7218,7 @@ Debug Message: - + None @@ -7232,722 +7244,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7955,74 +7982,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/de.ts b/dist/languages/de.ts index ee57558cee..a911437c44 100644 --- a/dist/languages/de.ts +++ b/dist/languages/de.ts @@ -591,12 +591,22 @@ SMAA bietet die beste Bildqualität. FXAA liefert bei niedrigeren Auflösungen ein stabileres Bild. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Vollbild-Modus: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -605,12 +615,12 @@ Der randlose Modus bietet die beste Kompatibilität mit der Bildschirmtastatur, Der exklusive Vollbildmodus bietet möglicherweise eine bessere Leistung und eine bessere Freesync-/G-Sync-Unterstützung. - + Aspect Ratio: Seitenverhältnis: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -619,36 +629,36 @@ Die meisten Spiele unterstützen nur das Seitenverhältnis 16:9, sodass Anpassun Steuert außerdem das Seitenverhältnis der aufgenommenen Bildschirmfotos. - + Use persistent pipeline cache Persistenten Rohrleitungs-Cache verwenden - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Ermöglicht das Speichern von Shadern, um sie beim nächsten Spielstart schneller laden zu können. Die Deaktivierung ist ausschließlich für Fehlerbehebungszwecke vorgesehen. - + Use asynchronous GPU emulation Asynchrone GPU-Emulation verwenden - + Uses an extra CPU thread for rendering. This option should always remain enabled. Verwendet einen zusätzlichen CPU-Thread für das Rendern. Diese Option sollte immer aktiviert bleiben. - + NVDEC emulation: NVDEC-Emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -657,12 +667,12 @@ Dabei kann entweder die CPU oder die GPU für die Dekodierung verwendet werden, In den meisten Fällen bietet die GPU-Dekodierung die beste Leistung. - + ASTC Decoding Method: ASTC-Dekodier-Methode: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -675,12 +685,12 @@ CPU asynchron: Die Dekodierung von ASTC-Texturen erfolgt bei Bedarf über die CP kann jedoch zu Artefakten führen. - + ASTC Recompression Method: ASTC-Rekompression-Methode: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -689,44 +699,64 @@ BC1/BC3: Das Zwischenformat wird wieder in das BC1- oder BC3-Format komprimiert, was VRAM spart, jedoch zu Einbußen bei der Bildqualität führt. - + Frame Pacing Mode (Vulkan only) Frame-Pacing-Modus (nur Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Steuert, wie der Emulator das Frame-Pacing verwaltet, um Ruckeln zu reduzieren und die Bildrate flüssiger und gleichmäßiger zu gestalten. - + VRAM Usage Mode: VRAM-Nutzungs Modus: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Legt fest, ob der Emulator vorrangig Speicherplatz sparen oder den verfügbaren Grafikspeicher zur Leistungsoptimierung maximal ausnutzen soll. Der aggressive Modus kann die Leistung anderer Anwendungen, wie z. B. Aufnahmesoftware, beeinträchtigen. - + Skip CPU Inner Invalidation CPU-interne Invalidierung überspringen - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Dies verursacht vielleicht Abstürze. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: VSync-Modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -737,12 +767,12 @@ Postkasten kann eine geringere Latenz als FIFO aufweisen und verursacht kein Tea Sofort (ohne Synchronisation) zeigt alles an, was verfügbar ist, und kann Tearing verursachen. - + Sync Memory Operations Speicheroperationen synchronisieren - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -751,44 +781,44 @@ Diese Option behebt Probleme in Spielen, kann jedoch zu Leistungseinbußen führ Bei Spielen mit Unreal Engine 4 sind die Auswirkungen oft am deutlichsten spürbar. - + Enable asynchronous presentation (Vulkan only) Aktiviere asynchrone Präsentation (Nur Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Verbessert die Leistung geringfügig, indem die Darstellung auf einen separaten CPU-Thread verlagert wird. - + Force maximum clocks (Vulkan only) Erzwinge Maximale Taktrate (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Lässt im Hintergrund die GPU Aufgaben erledigen während diese auf Grafikbefehle wartet, damit diese nicht herunter taktet. - + Anisotropic Filtering: Anisotrope Filterung: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Steuert die Qualität der Texturwiedergabe bei schrägen Blickwinkeln. Bei den meisten Grafikkarten kann dieser Wert bedenkenlos auf 16x eingestellt werden. - + GPU Mode: GPU-Modus: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -797,12 +827,12 @@ Die meisten Spiele werden im Modus Schnell in Ordnung gerendert, für einige ist Partikel werden in der Regel nur im Modus Akkurat korrekt gerendert. - + DMA Accuracy: DMA-Genauigkeit: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -811,76 +841,58 @@ Unsicher ist schneller, während Sicher stabiler ist und in manchen Spielen Prob Standard folgt der GPU-Genauigkeit Einstellung. - + GPU Fence Behavior: GPU-Zaunobjekt-Verhalten: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. - Steuert das GPU-Zaunobjekt-Synchronisations-Verhalten. -Direkt ist die schnellste Option, kann aber zu Problemen führen. -Ausbalanciert bietet bessere Kompatibilität und kann in manchen Spielen Probleme beheben. -Genau bietet noch bessere Kompatibilität auf Kosten von etwas Leisung. -Strikt ist die langsamste Option, kann aber Probleme beheben, die striktere Synchronisation erfordern. -Standard folgt der GPU-Genauigkeit Einstellung. +Default follows the GPU Mode setting. + - - Enable GPU buffer readback - GPU-Puffer-Rücklesung aktivieren - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - Bewahrt von der GPU modifizierte Pufferdaten, indem diese vor dem Hochladen zurückgelesen werden. -Einige Spiele benötigen dies, um bestimmte Effekte korrekt darzustellen. - - - + Enable asynchronous shader compilation Aktiviere asynchrones Shader-Kompilieren - + May reduce shader stutter. Kann Shader-Ruckler reduzieren. - + GPU Clocks GPU-Takte - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. Führt dazu, dass das Spiel glaubt, die Arbeit der GPU würde früher fertig sein, als sie eigentlich ist. Dadurch hört das Spiel auf die Auflösung und Render-Distanz zu verringern um sich den Takten der Switch anzupassen. - + GPU Unswizzle GPU-Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Beschleunigt die Dekodierung von BCn-3D-Texturen mithilfe von GPU-Rechenleistung. Deaktiviere diese Option, falls Abstürze oder Grafikfehler auftreten. - + GPU Unswizzle Max Texture Size GPU-Unswizzle max. Texturgröße - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -889,48 +901,48 @@ Während die GPU für mittelgroße und große Texturen schneller ist, kann die C Passen Sie diesen Wert an, um das Gleichgewicht zwischen GPU-Beschleunigung und CPU-Overhead zu finden. - + GPU Unswizzle Stream Size GPU-Unswizzle-Streamgröße - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Legt die maximale Menge an Texturdaten (in MiB) fest, die pro Bild verarbeitet wird. Höhere Werte können Ruckeln beim Laden von Texturen verringern, können sich jedoch auf die Bildrate auswirken. - + GPU Unswizzle Chunk Size GPU-Unswizzle Chunk-Größe - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Legt die Anzahl der Tiefenschnitte fest, die in einem einzelnen Durchlauf verarbeitet werden. Eine Erhöhung dieses Werts kann den Durchsatz auf High-End-GPUs verbessern, bei schwächerer Hardware jedoch zu TDR- oder Treiber-Auszeiten führen. - + Use Vulkan pipeline cache Vulkan-Pipeline-Cache verwenden - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Aktiviert den herstellerspezifischen Pipeline-Cache der GPU. Diese Option kann die Ladezeit von Shadern erheblich verkürzen, wenn der Vulkan-Treiber die Pipeline-Cache-Dateien nicht intern speichert. - + Enable Compute Pipelines (Intel Vulkan Only) Aktiviere Compute-Pipelines (Nur Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -939,194 +951,182 @@ Diese Einstellung ist nur bei proprietären Intel-Treibern vorhanden und kann be Bei allen anderen Treibern sind die Rechen-Rohrleitungen immer aktiviert. - + Enable Reactive Flushing Aktiviere Reactives Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Benutzt Reactive-Flushing anstatt Predictive-Flushing, welches akkurateres Speicher-Synchronisieren erlaubt. - + Sync to framerate of video playback Synchronisiere mit Bildrate von Video-Wiedergaben - + Run the game at normal speed during video playback, even when the framerate is unlocked. Lasse das Spiel in der normalen Geschwindigkeit abspielen, trotz freigeschalteter Bildrate - + Barrier feedback loops Barrier-Feedback-Schleifen - + Improves rendering of transparency effects in specific games. Verbessert das Rendering von Transparenzeffekten in bestimmten Spielen. - + Enable buffer history Puffer-Verlauf aktivieren - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Ermöglicht den Zugriff auf frühere Pufferzustände. Diese Option kann in manchen Spielen die Darstellungsqualität und die Leistungsstabilität verbessern. - + Fix bloom effects Bloom-Effekte beheben - + Removes bloom in Burnout. Entfernt Bloom in Burnout. - - Enable Legacy Rescale Pass - „Legacy Rescale Pass“ aktivieren - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Behebt möglicherweise Probleme mit der Skalierung in einigen Spielen, indem auf das Verhalten der vorherigen Implementierung zurückgegriffen wird. -Eine Umgehungslösung für das alte Verhalten, die Linienartefakte auf AMD- und Intel-GPUs sowie graues Texturflackern auf Nvidia-GPUs in „Luigi’s Mansion 3“ behebt. - - - + Extended Dynamic State Erweiterter dynamischer Zustand - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Legt die Anzahl der Funktionen fest, die im erweiterten dynamischen Modus verwendet werden können. Höhere Stufen ermöglichen mehr Funktionen und können die Leistung steigern, können jedoch zusätzliche grafische Probleme verursachen. - + Vertex Input Dynamic State Dynamischer Zustand der Scheitelpunkteingabe - + Enables vertex input dynamic state feature for better quality and performance. Aktiviert die Funktion für den dynamischen Status der Scheitelpunkteingabe, um eine bessere Qualität und Leistung zu erzielen. - + Sample Shading Probe-Schattierung - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Ermöglicht es dem Fragment-Shader, pro Abtastwert in einem mehrfach abgetasteten Fragment ausgeführt zu werden, anstatt nur einmal pro Fragment. Verbessert die Grafikqualität auf Kosten der Leistung. Höhere Werte verbessern die Qualität, beeinträchtigen jedoch die Leistung. - + RNG Seed Startwert für den Zufallszahlengenerator - + Controls the seed of the random number generator. Mainly used for speedrunning. Steuert den Startwert des Zufallszahlengenerators. Wird hauptsächlich für Schnelldurchläufe verwendet. - + Device Name Gerätename - + The name of the console. Der Name der Konsole. - + Homebrew Args Homebrew-Argumente - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). Befehlszeilenargumente, die beim Start an Homebrew übergeben werden (z. B. -noglsl). - + Custom RTC Date: Benutzerdefinierte Echtzeituhrdatum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Diese Option erlaubt die Änderung der Uhr der Konsole. Kann benutzt werden um Zeit in Spielen zu manipulieren. - + The number of seconds from the current unix time Die Anzahl der Sekunden seit dem aktuellen Unix-Zeitstempel - + Language: Sprache: - + This option can be overridden when region setting is auto-select Diese Option kann überschrieben werden, wenn die Regionseinstellung auf „Automatische Auswahl“ steht - + Region: Region: - + The region of the console. Die Region der Konsole. - + Time Zone: Zeitzone: - + The time zone of the console. Die Zeitzone der Konsole. - + Sound Output Mode: Tonausgangsmodus: - + Console Mode: Konsolenmodus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1135,59 +1135,47 @@ Je nach dieser Einstellung ändern sich bei Spielen die Auflösung, die Detailst Die Einstellung auf „Handgerät“ kann dazu beitragen, die Leistung auf Systemen mit geringerer Leistung zu verbessern. - + Prompt for user profile on boot Beim Systemstart nach dem Benutzerprofil fragen - + Useful if multiple people use the same PC. Nützlich, wenn mehrere Personen denselben PC nutzen. - + Pause when not in focus Pausieren, wenn nicht im Fokus - + Pauses emulation when focusing on other windows. Pausiert die Emulation, wenn der Fokus auf andere Fenster wechselt. - + Confirm before stopping emulation Vor dem Stoppen der Emulation bestätigen - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Überschreibt die Abfragen zur Bestätigung des Beendens der Emulation. Wenn diese Option aktiviert ist, werden solche Abfragen umgangen und die Emulation wird direkt beendet. - + Hide mouse on inactivity Mauszeiger verbergen - + Hides the mouse after 2.5s of inactivity. Verbirgt den Mauszeiger nach 2,5 Sekunden Inaktivität. - - - Disable controller applet - Controller-Applet deaktivieren - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Deaktiviert die Verwendung des Controller-Applets in emulierten Programmen zwangsweise. -Wenn ein Programm versucht, das Controller-Applet zu öffnen, wird es sofort geschlossen. - Check for updates @@ -1219,155 +1207,156 @@ Wenn ein Programm versucht, das Controller-Applet zu öffnen, wird es sofort ges Echtes Applet - + + + Disabled + Deaktiviert + + + Never Niemals - + On Load Beim Laden - + Always Immer - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Asynchron - + Uncompressed (Best quality) Unkomprimiert (Beste Qualität) - + BC1 (Low quality) BC1 (Niedrige Qualität) - + BC3 (Medium quality) BC3 (Mittlere Qualität) - + Auto Auto - + 30 FPS 30 BpS - + 60 FPS 60 BpS - + 90 FPS 90 BpS - + 120 FPS 120 BpS - + Conservative Konservativ - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Assembler-Shader, nur NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (experimentell, nur AMD/Mesa) - + Null Null - + Fast Schnell - - + + Accurate Akkurat - - + + Default Vorgabe - + Unsafe (fast) Unsicher (schnell) - + Safe (stable) Sicher (stabil) - + Immediate Direkt - + Balanced Ausgeglichen - - - Strict - Strikt - Unsafe @@ -2144,11 +2133,6 @@ Wenn ein Programm versucht, das Controller-Applet zu öffnen, wird es sofort ges High (512) Hoch (512) - - - Disabled - Deaktiviert - ExtendedDynamicState 1 @@ -3282,33 +3266,38 @@ Möchtest du die alten Speicherdaten löschen? Hintergrundfarbe: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + + + + Off Aus - + VSync Off Vsync Aus - + Recommended Empfohlen - + On An - + VSync On Vsync An @@ -4935,102 +4924,117 @@ Einmal gelöscht, können diese nicht wiederhergestellt werden. Bist du dir zu 1 - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7047,7 +7051,7 @@ Fehlerbehebungs-Meldung: - + &Pause &Pause @@ -7198,7 +7202,7 @@ Fehlerbehebungs-Meldung: - + &Start &Start @@ -7209,7 +7213,7 @@ Fehlerbehebungs-Meldung: - + R&ecord Aufnahme @@ -7300,7 +7304,7 @@ Fehlerbehebungs-Meldung: - + None Keine @@ -7326,400 +7330,405 @@ Fehlerbehebungs-Meldung: &Karussellansicht - + Small (32x32) Klein (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Groß (128x128) - + Full Size (256x256) Volle Größe (256x256) - + Broken Vulkan Installation Detected Defekte Vulkan-Installation erkannt - + Vulkan initialization failed during boot. Initialisierung von Vulkan ist beim Systemstart fehlgeschlagen. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Spiel wird ausgeführt - + Loading Web Applet... Web-Applet wird geladen... - - + + Disable Web Applet Web-Applet deaktivieren - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Das Deaktivieren des Web-Applets kann zu unvorhersehbarem Verhalten führen und sollte nur bei „Super Mario 3D All-Stars“ verwendet werden. Möchtest du das Web-Applet wirklich deaktivieren? (Dies kann in den Fehlerbehebungs-Einstellungen wieder aktiviert werden.) - + The amount of shaders currently being built Die Anzahl der Shader, die derzeit erstellt werden - + The current selected resolution scaling multiplier. Der aktuell ausgewählte Skalierungsfaktor für die Auflösung. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Aktuelle Emulationsgeschwindigkeit. Werte über oder unter 100 % bedeuten, dass die Emulation schneller oder langsamer läuft als auf einer Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Wie viele Bilder pro Sekunde das Spiel derzeit anzeigt. Dies variiert von Spiel zu Spiel und von Szene zu Szene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Die Zeit, die für die Emulation eines Switch-Frames benötigt wird, ohne Berücksichtigung von Framelimiting oder V-Sync. Bei einer Emulation mit voller Geschwindigkeit sollte dieser Wert höchstens 16,67 ms betragen. - + Unmute Stummschaltung aufheben - + Mute Stummschalten - + Reset Volume Lautstärke zurücksetzen - + + Configure Effects... + + + + &Clear Recent Files &Zuletzt verwendete Dateien löschen - + &Continue &Fortfahren - + Warning: Outdated Game Format Warnung: Veraltetes Spielformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Du verwendest für dieses Spiel das dekontruierte ROM-Verzeichnisformat - ein veraltetes Format, das durch andere Formate wie NCA, NAX, XCI oder NSP abgelöst wurde. Dekonstruierten ROM-Verzeichnissen fehlen Symbole, Metadaten und die Unterstützung für Aktualisierungen. <br>Eine Erläuterung der verschiedenen Switch-Formate, die Eden unterstützt, findest du in unserem Benutzerhandbuch. Diese Meldung wird nicht erneut angezeigt. - - + + Error while loading ROM! Fehler beim Laden des ROMs! - + The ROM format is not supported. Das ROM-Format wird nicht unterstützt. - + An error occurred initializing the video core. Beim Initialisieren des Videokerns ist ein Fehler aufgetreten. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Fehler beim Laden des ROMs! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Bitte erstelle deine Dateien erneut oder bitte auf Discord/Stoat um Hilfe. - + An unknown error occurred. Please see the log for more details. Es ist ein unbekannter Fehler aufgetreten. Weitere Informationen findest du im Protokoll. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Software wird geschlossen... - + Save Data Speicherdaten - + Mod Data Mod-Daten - + Error Opening %1 Folder Fehler beim Öffnen des Ordners %1 - - + + Folder does not exist! Der Ordner existiert nicht! - + Remove Installed Game Contents? Installierte Spielinhalte entfernen? - + Remove Installed Game Update? Installierte Spielaktualisierung entfernen? - + Remove Installed Game DLC? Installiertes Spiel-DLC entfernen? - + Remove Entry Eintrag entfernen - + Delete OpenGL Transferable Shader Cache? Den übertragbaren Shader-Cache von OpenGL löschen? - + Delete Vulkan Transferable Shader Cache? Den übertragbaren Shader-Cache von Vulkan löschen? - + Delete All Transferable Shader Caches? Alle übertragbaren Shader-Caches löschen? - + Remove Custom Game Configuration? Benutzerdefinierte Spielkonfiguration entfernen? - + Remove Cache Storage? Cache-Speicher löschen? - + Remove File Datei entfernen - + Remove Play Time Data Wiedergabezeitdaten löschen - + Reset play time? Spielzeit zurücksetzen? - - + + RomFS Extraction Failed! RomFS-Extraktion fehlgeschlagen! - + There was an error copying the RomFS files or the user cancelled the operation. Beim Kopieren der RomFS-Dateien ist ein Fehler aufgetreten oder der Benutzer hat den Vorgang abgebrochen. - + Full Vollständig - + Skeleton Skelett - + Select RomFS Dump Mode RomFS-Auslesemodus auswählen - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Bitte wähle aus, wie das RomFS kopiert werden soll. <br>Vollständig kopiert alle Dateien in das neue Verzeichnis, während <br>Skelett lediglich die Verzeichnisstruktur erstellt. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Unter %1 ist nicht genügend Speicherplatz vorhanden, um das RomFS zu extrahieren. Bitte schaffe Speicherplatz oder wähle unter „Emulation > Konfigurieren > System > Dateisystem > Dump-Stammverzeichnis“ ein anderes Dump-Verzeichnis aus. - + Extracting RomFS... RomFS wird extrahiert... - - + + Cancel Abbrechen - + RomFS Extraction Succeeded! RomFS wurde erfolgreich extrahiert! - + The operation completed successfully. Der Vorgang wurde erfolgreich abgeschlossen. - + Error Opening %1 Fehler beim Öffnen von %1 - + Select Directory Verzeichnis auswählen - + Properties Eigenschaften - + The game properties could not be loaded. Die Spieleigenschaften konnten nicht geladen werden. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch-Ausführungsdatei (%1);;Alle Dateien (*.*) - + Load File Datei laden - + Open Extracted ROM Directory Verzeichnis mit dem extrahierten ROM öffnen - + Invalid Directory Selected Ungültiges Verzeichnis ausgewählt - + The directory you have selected does not contain a 'main' file. Das von dir ausgewählte Verzeichnis enthält keine „main“-Datei. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installierbare Switch-Dateien (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX-Kassetten-Image (*.xci) - + Install Files Dateien installieren - + %n file(s) remaining %n Datei(en) verbleiben%n Datei(en) verbleiben - + Installing file "%1"... Datei „%1“ wird installiert... + - Install Results Installationsergebnisse - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Um mögliche Konflikte zu vermeiden, raten wir davon ab, Basisspiele auf dem NAND-Speicher zu installieren. Bitte nutze diese Funktion ausschließlich zur Installation von Aktualisierungen und DLCs. - + %n file(s) were newly installed %n Datei(en) wurden neu installiert @@ -7727,7 +7736,7 @@ Bitte nutze diese Funktion ausschließlich zur Installation von Aktualisierungen - + %n file(s) were overwritten %n Datei(en) wurden überschrieben @@ -7735,7 +7744,7 @@ Bitte nutze diese Funktion ausschließlich zur Installation von Aktualisierungen - + %n file(s) failed to install %n Datei(en) konnten nicht installiert werden @@ -7743,314 +7752,324 @@ Bitte nutze diese Funktion ausschließlich zur Installation von Aktualisierungen - + System Application Systemanwendung - + System Archive Systemarchiv - + System Application Update System-Anwendungsaktualisierung - + Firmware Package (Type A) Firmware-Paket (Typ A) - + Firmware Package (Type B) Firmware-Paket (Typ B) - + Game Spiel - + Game Update Spielaktualisierung - + Game DLC Spiel-DLC - + Delta Title Delta-Titel - + Select NCA Install Type... NCA-Installationstyp auswählen... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Bitte wähle den Titeltyp aus, unter dem du diese NCA installieren möchtest: (In den meisten Fällen ist die Vorgabe „Spiel“ ausreichend.) - + Failed to Install Installation fehlgeschlagen - + The title type you selected for the NCA is invalid. Der von dir für die NCA ausgewählte Titel ist ungültig. - + File not found Datei nicht gefunden - + File "%1" not found Datei „%1“ nicht gefunden - + OK OK - + Function Disabled Funktion deaktiviert - + Compatibility list reporting is currently disabled. Check back later! Die Anzeige der Kompatibilitätsliste ist derzeit deaktiviert. Schaue später noch einmal vorbei! - + Error opening URL Fehler beim Öffnen der URL - + Unable to open the URL "%1". Die URL „%1“ kann nicht geöffnet werden. - + TAS Recording TAS-Aufnahme - + Overwrite file of player 1? Datei von Spieler 1 überschreiben? - + Invalid config detected Ungültige Konfiguration erkannt - + Handheld controller can't be used on docked mode. Pro controller will be selected. Der Handgerät-Controller kann im angedockten Modus nicht verwendet werden. Es wird der Pro-Controller ausgewählt. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Das aktuelle amiibo wurde entfernt - + Error Fehler - - + + The current game is not looking for amiibos Das aktuelle Spiel sucht nicht nach Amiibos - + Amiibo File (%1);; All Files (*.*) Amiibo-Datei (%1);; Alle Dateien (*.*) - + Load Amiibo Amiibo laden - + Error loading Amiibo data Fehler beim Laden der Amiibo-Daten - + The selected file is not a valid amiibo Die ausgewählte Datei ist kein gültiges amiibo - + The selected file is already on use Die ausgewählte Datei wird bereits verwendet - + An unknown error occurred Ein unbekannter Fehler ist aufgetreten - + No firmware available Keine Firmware verfügbar - + Firmware Corrupted Firmware beschädigt - + Unknown applet Unbekanntes Applet - + Applet doesn't map to a known value. „Applet“ lässt sich keinem bekannten Wert zuordnen. - + Record not found Aufnahme nicht gefunden - + Applet not found. Please reinstall firmware. Applet nicht gefunden. Bitte installiere die Firmware neu. - + Capture Screenshot Bildschirmfoto aufnehmen - + PNG Image (*.png) PNG-Bild (*.png) - + TAS state: Running %1/%2 TAS-Status: %1/%2 läuft - + TAS state: Recording %1 TAS-Status: %1 wird aufgenommen - + TAS state: Idle %1/%2 TAS-Status: Leerlauf %1/%2 - + TAS State: Invalid TAS-Status: Ungültig - + &Stop Running &Ausführung beenden - + Stop R&ecording Aufnahme beenden - + Building: %n shader(s) Erstellung: %n SchattiererErstelle: %n Shader - + Scale: %1x %1 is the resolution scaling factor Maßstab: 1:1 - + Speed: %1% / %2% Geschwindigkeit: %1% / %2% - + Speed: %1% Geschwindigkeit: %1% - + Game: %1 FPS Spiel: %1 FPS - + Frame: %1 ms Frame: %1 ms - + NO AA Kein AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE LAUTSTÄRKE: STUMM - + VOLUME: %1% Volume percentage (e.g. 50%) LAUTSTÄRKE: %1% - + Derivation Components Missing Ableitungskomponenten fehlen - + Decryption keys are missing. Install them now? Entschlüsselungsschlüssel fehlen. Möchtest du diese jetzt installieren? - + Wayland Detected! Wayland erkannt! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8061,74 +8080,74 @@ Es wird empfohlen, stattdessen X11 zu verwenden. Möchtest du dies für zukünftige Starts erzwingen? - + Use X11 X11 verwenden - + Continue with Wayland Mit Wayland fortfahren - + Don't show again Nicht mehr anzeigen - + Restart Required Neustart erforderlich - + Restart Eden to apply the X11 backend. Starte Eden neu, um das X11-Backend zu aktivieren. - + Slow Langsam - + Turbo Turbo - + Unlocked Entsperrt - + Select RomFS Dump Target RomFS-Ausleseziel auswählen - + Please select which RomFS you would like to dump. Bitte wähle aus, welches RomFS du auslesen möchtest. - + Are you sure you want to close Eden? Möchtest du Eden wirklich schließen? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Möchtest du die Emulation wirklich beenden? Alle nicht gespeicherten Fortschritte gehen dabei verloren. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/el.ts b/dist/languages/el.ts index 50ec19b094..b3ec9c6f5e 100644 --- a/dist/languages/el.ts +++ b/dist/languages/el.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Λειτουργία Πλήρους Οθόνης: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Αναλογία Απεικόνισης: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Εξομοίωση NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: Ανισοτροπικό Φιλτράρισμα: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Περιφέρεια: - + The region of the console. - + Time Zone: Ζώνη Ώρας: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Απόκρυψη δρομέα ποντικιού στην αδράνεια - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - + Auto Αυτόματη - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - - + + Accurate Ακριβής - - + + Default Προεπιλεγμένο - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3190,33 +3182,38 @@ Would you like to delete the old save data? Χρώμα Φόντου: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4837,102 +4834,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6944,7 +6956,7 @@ Debug Message: - + &Pause &Παύση @@ -7095,7 +7107,7 @@ Debug Message: - + &Start &Έναρξη @@ -7106,7 +7118,7 @@ Debug Message: - + R&ecord @@ -7197,7 +7209,7 @@ Debug Message: - + None @@ -7223,722 +7235,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7946,74 +7973,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/es.ts b/dist/languages/es.ts index d12a8c2b36..8cda19bc6b 100644 --- a/dist/languages/es.ts +++ b/dist/languages/es.ts @@ -592,12 +592,22 @@ SMAA ofrece mejor calidad. FXAA puede producir mejores resultados visuales en resoluciones bajas. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Modo pantalla completa: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,12 +616,12 @@ Ventana sin bordes ofrece la mejor compatibilidad con el teclado en pantalla que Pantalla completa exclusiva puede ofrecer mejor rendimiento y mejor soporte para FreeSync/G-Sync/VRR. - + Aspect Ratio: Relación de aspecto: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -620,36 +630,36 @@ La mayoría de los juegos solo admiten 16:9, por lo que se requieren modificacio También controla la relación de aspecto de las capturas de pantalla. - + Use persistent pipeline cache Usar el caché de canalización persistente - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Permite almacenar los sombreadores para que carguen más rápido la próxima vez que inicies el juego. Desactivarlo solo está destinado para depuración. - + Use asynchronous GPU emulation Usar emulación asíncrona de la GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Usa un hilo adicional de la CPU para el renderizado. Esta opción siempre debe permanecer activada. - + NVDEC emulation: Emulación NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -658,12 +668,12 @@ Puede usar la CPU, GPU o no descodificar (mostrará una pantalla en negro durant En la mayoría de casos, descodificar mediante la GPU es la mejor opción. - + ASTC Decoding Method: Modo descodificación ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -675,12 +685,12 @@ GPU: utiliza los sombreadores computables de la GPU para descodificar las textur CPU asíncrona: usa la CPU para descodificar las texturas ASTC bajo demanda. Elimina los tirones causados por la descodificación ASTC, pero puede generar errores visuales. - + ASTC Recompression Method: Modo recompresión ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -688,44 +698,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: El formato intermedio será recomprimido a BC1 o BC3, lo que ahorra VRAM, pero degrada la calidad de la imagen. - + Frame Pacing Mode (Vulkan only) Modo de ritmo de fotogramas (solo Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Controla cómo el emulador gestiona el ritmo de los fotogramas para reducir los tirones y hacer que la velocidad de los fotogramas sea más suave y consistente. - + VRAM Usage Mode: Modo de uso de la VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Selecciona si el emulador prefiere ahorrar memoria o maximizar el uso de memoria de video disponible para mejorar el rendimiento. El modo agresivo puede afectar al rendimiento de otras aplicaciones, como a los programas de grabación. - + Skip CPU Inner Invalidation Saltar invalidación interna de la CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Omite ciertas invalidaciones de caché durante las actualizaciones de memoria, reduciendo el uso de la CPU y mejorando la latencia. Esto puede causar bloqueos leves. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Modo VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -736,12 +766,12 @@ Mailbox ofrece menor latencia que FIFO y evita el tearing, aunque puede perder a Immediate (sin sincronización) muestra los fotogramas tan pronto están disponibles, pero puede generar desgarros de imagen. - + Sync Memory Operations Sincronizar operaciones de memoria - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -750,56 +780,56 @@ Esta opción arregla errores en los juegos, pero puede afectar negativamente al Los juegos con Unreal Engine 4 son con frecuencia los que muestran cambios significantes en esto. - + Enable asynchronous presentation (Vulkan only) Activar presentación asíncrona (solo Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Mejora el rendimiento ligeramente al usar un hilo de CPU adicional para la presentación. - + Force maximum clocks (Vulkan only) Forzar relojes al máximo (solo Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Ejecuta los procesos en segundo plano mientras se espera a las instrucciones gráficas para evitar que la GPU reduzca su velocidad de reloj. - + Anisotropic Filtering: Filtrado anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controla la calidad del renderizado de texturas en ángulos oblicuos. Es seguro configurarlo en 16x en la mayoría de las GPU. - + GPU Mode: Modo de la GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Precisión de DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -808,70 +838,58 @@ Inseguro es más rápido, mientras que seguro es más estable y puede solucionar Predeterminado sigue el ajuste de la precisión de GPU. - + GPU Fence Behavior: Comportamiento de vallado de la GPU: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - Activar la lectura del búfer de la GPU - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Activa la compilación de shaders asincrónicos - + May reduce shader stutter. Puede reducir los tirones causados por la carga de sombreadores. - + GPU Clocks Relojes de GPU - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle Desentrelazado de la GPU - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Acelera la decodificación de texturas 3D BCn mediante computación de la GPU. Desactívela si experimenta problemas o fallos gráficos. - + GPU Unswizzle Max Texture Size Tamaño máximo de textura de desentrelazado de la GPU - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -880,46 +898,46 @@ Aunque la GPU es más rápida para texturas medianas y grandes, la CPU puede ser Ajuste este valor para encontrar el equilibrio entre la aceleración de la GPU y la sobrecarga de la CPU. - + GPU Unswizzle Stream Size Tamaño del flujo de desentrelazado de la GPU - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Establece la cantidad máxima de datos de textura (en MiB) procesados ​​por cada fotograma. Valores más altos pueden reducir la distorsión durante la carga de texturas, pero pueden afectar a la consistencia de los fotogramas. - + GPU Unswizzle Chunk Size Tamaño del trozo de desentrelazado de la GPU - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Determina la cantidad de cortes de profundidad procesados ​​en un solo envío. Aumentar este valor puede mejorar el rendimiento en una GPU de gama alta, pero puede causar tirones y problemas en los tiempos de respuesta en hardware más modesto. - + Use Vulkan pipeline cache Usar caché de canalización de Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Activa la caché de canalización específica del fabricante. Esta opción puede mejorar los tiempos de cargas de sombreadores en el caso de que el controlador de Vulkan no lo almacene internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Activar la canalizaciones de cómputo (solo Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -928,194 +946,182 @@ Este ajuste solo existe para los controladores propietarios de Intel y puede cau En los demás controladores, la canalización de cómputo siempre está activada. - + Enable Reactive Flushing Activar limpieza reactiva - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa limpieza de memoria reactiva en vez de predictiva, permitiendo una sincronización de memoria más precisa. - + Sync to framerate of video playback Sincronizar a fotogramas de reproducción de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Ejecuta el juego a velocidad normal durante la reproducción de vídeos, incluso cuando no hay límite de fotogramas. - + Barrier feedback loops Bucles de feedback de barrera - + Improves rendering of transparency effects in specific games. Mejora la renderización de los efectos de transparencia en ciertos juegos. - + Enable buffer history Activar el historial del búfer - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Habilita el acceso a estados de búfer anteriores. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos. - + Fix bloom effects Arreglar efectos de resplandor - + Removes bloom in Burnout. Elimina el resplandor en Burnout. - - Enable Legacy Rescale Pass - Activar el pase de reescalado heredado - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Puede arreglar los problemas de reescalado en algunos juegos confiando en el comportamiento de la implementación anterior. -Solución alternativa de comportamiento heredado que corrige los artefactos de línea de la GPU AMD y el parpadeo de texturas gris de la GPU Nvidia en Luigis Mansion 3. - - - + Extended Dynamic State Estado dinámico extendido - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controla el número de funciones que pueden ser usadas en el Estado Dinámico Extendido. Números más altos permiten úsar más funciones y pueden aumentar el rendimiento, pero tambén pueden causar errores gráficos. - + Vertex Input Dynamic State Estado dinámico de entrada de vértices - + Enables vertex input dynamic state feature for better quality and performance. Activa la función de estado dinámico de entrada de vértices para una mejor calidad y rendimiento. - + Sample Shading Sombreado de muestra - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permite que el sombreador de fragmentos se ejecute por muestra en un fragmento multimuestreado, en lugar de una sola vez por fragmento. Mejora la calidad de los gráficos a costa de rendimiento. Los valores más altos mejoran la calidad, pero reducen el rendimiento. - + RNG Seed Semilla de GNA - + Controls the seed of the random number generator. Mainly used for speedrunning. Controla la semilla del generador de números aleatorios. Usado principalmente para speedrunning. - + Device Name Nombre del dispositivo - + The name of the console. El nombre de la consola - + Homebrew Args Argumentos Homebrew - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). Argumentos en la línea de comandos pasados al homebrew al ser lanzado (p.e. -noglsl). - + Custom RTC Date: Fecha Personalizada RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Esta opción permite cambiar el reloj de la consola. Puede ser usado para manipular el tiempo en juegos. - + The number of seconds from the current unix time Número de segundos de la hora actual de Unix - + Language: Idioma: - + This option can be overridden when region setting is auto-select Esta opción puede ser reemplazada cuando la configuración de región está en selección automática. - + Region: Región: - + The region of the console. La Región de la Consola. - + Time Zone: Zona horaria: - + The time zone of the console. La zona horaria de la consola. - + Sound Output Mode: Método de salida de sonido: - + Console Mode: Modo consola: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1124,58 +1130,46 @@ Los juegos cambiarán su resolución, detalles y compatibilidad con los mandos s Configurar el modo portátil puede mejorar el rendimiento en sistemas de gama baja. - + Prompt for user profile on boot Solicitud para perfil de usuario al arrancar - + Useful if multiple people use the same PC. Útil si múltiples personas usan la misma PC - + Pause when not in focus Pausar cuando la ventana no esté activa - + Pauses emulation when focusing on other windows. Pausa la emulación cuando otra ventana diferente está activa. - + Confirm before stopping emulation Confirmar detención - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Anula las solicitudes de confirmación para detener la emulación. Al habilitar esta opción, se omiten dichas solicitudes y se sale directamente de la emulación. - + Hide mouse on inactivity Ocultar el cursor por inactividad. - + Hides the mouse after 2.5s of inactivity. Oculta el mouse después de 2.5s de inactividad. - - - Disable controller applet - Desactivar applet de mandos - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Desactiva forzosamente el uso del applet del controlador en programas emulados. -Cuando un programa intenta abrir el applet del controlador, se cierra inmediatamente. - Check for updates @@ -1207,155 +1201,156 @@ Cuando un programa intenta abrir el applet del controlador, se cierra inmediatam Applet real - + + + Disabled + Desactivado + + + Never Nunca - + On Load Al cargar - + Always Siempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU asíncrona - + Uncompressed (Best quality) Sin compresión (Mejor calidad) - + BC1 (Low quality) BC1 (Calidad baja) - + BC3 (Medium quality) BC3 (Calidad media) - + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservativo - + Aggressive Agresivo - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Ensamblado de sombreadores, solo NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, solo AMD/Mesa) - + Null Ninguno - + Fast Rápido - - + + Accurate Preciso - - + + Default Predeterminado - + Unsafe (fast) Inseguro (rápido) - + Safe (stable) Seguro (estable) - + Immediate Inmediato - + Balanced Balanceado - - - Strict - Estricto - Unsafe @@ -2132,11 +2127,6 @@ Cuando un programa intenta abrir el applet del controlador, se cierra inmediatam High (512) Alto (512) - - - Disabled - Desactivado - ExtendedDynamicState 1 @@ -3270,33 +3260,38 @@ Would you like to delete the old save data? Color de fondo: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + Configurar efectos... + + + Off Desactivado - + VSync Off VSync Desactivado - + Recommended Recomendado - + On Activado - + VSync On VSync Activado @@ -4922,102 +4917,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply Aplicar - + Reset Values Restablecer valores - + Return every effect to the values the preset ships with. - + Save As... Guardar como... - + Save Preset - + Preset name: - - Delete - Borrar + + Clear + - + + Remove every effect in use and deselect the preset. + + + + Add Effect Añadir efecto - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled Activado - + Disabled Desactivado - + Reset to defaults Restaurar los valores predeterminados - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7035,7 +7045,7 @@ Mensaje de depuración: - + &Pause &Pausar @@ -7186,7 +7196,7 @@ Mensaje de depuración: - + &Start &Iniciar @@ -7197,7 +7207,7 @@ Mensaje de depuración: - + R&ecord G&rabar @@ -7288,7 +7298,7 @@ Mensaje de depuración: - + None Nada @@ -7314,400 +7324,405 @@ Mensaje de depuración: Vista de &carrusel - + Small (32x32) Pequeño (32x32) - + Standard (64x64) Estandar (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Tamaño completo (256x256) - + Broken Vulkan Installation Detected Detectada instalación fallida de Vulkan - + Vulkan initialization failed during boot. La inicialización de Vulkan falló durante el arranque. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Ejecutando un juego - + Loading Web Applet... Cargando applet web... - - + + Disable Web Applet Desactivar applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Desactivar el applet web puede causar comportamientos inesperados y debería solo ser usado con Super Mario 3D All-Stars. ¿Está seguro de que quiere desactivar el applet web? (Puede ser activado en las ajustes de depuración.) - + The amount of shaders currently being built La cantidad de sombreadores que se están contruyendo actualmente - + The current selected resolution scaling multiplier. El multiplicador de escalado de resolución seleccionado actualmente. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidad de emulación actual. Los valores superiores o inferiores al 100% indican que la emulación se ejecuta más rápida o más lenta que en una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. La cantidad de fotogramas por segundo que muestra el juego actualmente. Esto varía según el juego y la escena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tiempo empleado en emular un fotograma de la Switch, sin contar la limitación de fotogramas ni la sincronización vertical. Para una emulación a máxima velocidad, debería ser de 16,67 ms como máximo. - + Unmute Desilenciar - + Mute Silenciar - + Reset Volume Restablecer volumen - + + Configure Effects... + + + + &Clear Recent Files &Limpiar archivos recientes - + &Continue &Continuar - + Warning: Outdated Game Format Advertencia: formato del juego obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Estás usando el formato de directorio ROM deconstruido para este juego, el cual es un formato obsoleto que ha sido reemplazado por otros como NCA, NAX, XCI o NSP. Los directorios ROM deconstruidos carecen de iconos, metadatos y compatibilidad con actualizaciones.<br>Para obtener una explicación de los distintos formatos de Switch compatibles con Eden, consulta nuestro manual de usuario. Este mensaje no volverá a aparecer de nuevo. - - + + Error while loading ROM! ¡Error al cargar la ROM! - + The ROM format is not supported. El formato de la ROM no está soportado. - + An error occurred initializing the video core. Se produjo un error al inicializar el núcleo de video. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ¡Error al cargar la ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Por favor vuelva a volcar sus archivos, o pida ayuda en Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Ha ocurrido un error desconocido. Por favor mire el registro para más detalles. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Cerrando el programa... - + Save Data Datos de guardado - + Mod Data Datos del mod - + Error Opening %1 Folder Error abriendo la carpeta %1 - - + + Folder does not exist! ¡La carpeta no existe! - + Remove Installed Game Contents? ¿Eliminar el contenido instalado del juego? - + Remove Installed Game Update? ¿Eliminar la actualización instalada del juego? - + Remove Installed Game DLC? ¿Eliminar el contenido descargable instalado del juego? - + Remove Entry Eliminar entrada - + Delete OpenGL Transferable Shader Cache? ¿Desea eliminar el caché transferible de sombreadores de OpenGL? - + Delete Vulkan Transferable Shader Cache? ¿Desea eliminar el caché transferible de sombreadores de Vulkan? - + Delete All Transferable Shader Caches? ¿Desea eliminar todo el caché transferible de sombreadores? - + Remove Custom Game Configuration? ¿Borrar configuración personalizada del juego? - + Remove Cache Storage? ¿Borrar el almacenamiento de caché? - + Remove File Eliminar archivo - + Remove Play Time Data Eliminar datos del tiempo de juego - + Reset play time? ¿Reiniciar el tiempo de juego? - - + + RomFS Extraction Failed! ¡Fallo al extraer el RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Ocurrió un error al copiar los archivos de la RomFS o el usuario canceló la operación. - + Full Completo - + Skeleton Esqueleto - + Select RomFS Dump Mode Seleccione el método de volcado del RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Por favor selecciona como quieres que sea volcada la RomFS.<br>Completa copiará todos los archivos en el nuevo directorio mientras que <br>esqueleto solo creará la estructura de los directorios. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root No hay suficiente espacio libre en %1 para extraer la RomFS. Por favor libera espacio o selecciona un directorio de volcado diferente en Emulación > Configurar > Sistema > Sistema de archivos > Volcado raiz - + Extracting RomFS... Extrayendo el RomFS... - - + + Cancel Cancelar - + RomFS Extraction Succeeded! ¡La extracción del RomFS ha sido un éxito! - + The operation completed successfully. La operación se completó con éxito. - + Error Opening %1 Error al abrir %1 - + Select Directory Seleccionar directorio - + Properties Propiedades - + The game properties could not be loaded. No se pudieron cargar las propiedades del juego. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Ejecutable de Switch (%1);;Todos los archivos (*.*) - + Load File Cargar archivo - + Open Extracted ROM Directory Abrir directorio de la ROM extraída - + Invalid Directory Selected Seleccionado directorio inválido - + The directory you have selected does not contain a 'main' file. El directorio seleccionado no contiene un archivo 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Archivo de Switch instalable (*.nca *.nsp *.xci);;Archivo de contenido de Nintendo (*.nca);;Paquete de envío de Nintendo (*.nsp);;Imagen de cartucho NX (*.xci) - + Install Files Instalar archivos - + %n file(s) remaining %n archivo(s) restantes%n archivo(s) restantes%n archivo(s) restantes - + Installing file "%1"... Instalando el archivo "%1"... + - Install Results Resultados de la instalación - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Para evitar posibles conflictos, desaconsejamos instalar juegos base en la NAND. Por favor, use solo esta función para instalar actualizaciones y contenido descargable. - + %n file(s) were newly installed %n archivo(s) se instalaron recientemente @@ -7716,7 +7731,7 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + %n file(s) were overwritten %n archivo(s) fueron sobreescritos @@ -7725,7 +7740,7 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + %n file(s) failed to install fallo al instalar %n archivo(s) @@ -7734,314 +7749,324 @@ Por favor, use solo esta función para instalar actualizaciones y contenido desc - + System Application Aplicación del sistema - + System Archive Archivo del sistema - + System Application Update Actualización de la aplicación del sistema - + Firmware Package (Type A) Paquete de firmware (Tipo A) - + Firmware Package (Type B) Paquete de firmware (Tipo B) - + Game Juego - + Game Update Actualización del juego - + Game DLC Contenido descargable del juego - + Delta Title Título Delta - + Select NCA Install Type... Seleccione el tipo de instalación NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleccione el tipo de título en el que desea instalar este NCA: (En la mayoría de los casos, la opción predeterminada "Juego" es suficiente). - + Failed to Install Fallo al instalar - + The title type you selected for the NCA is invalid. El tipo de título que ha seleccionado para el NCA es inválido. - + File not found Archivo no encontrado - + File "%1" not found Archivo "%1" no encontrado - + OK Aceptar - + Function Disabled Función desactivada - + Compatibility list reporting is currently disabled. Check back later! Los informes de la lista de compatibilidad están deshabilitados. ¡Vuelva más tarde! - + Error opening URL Error abriendo URL - + Unable to open the URL "%1". No se pudo abrir URL "%1". - + TAS Recording Grabación TAS - + Overwrite file of player 1? ¿Sobrescribir el archivo del jugador 1? - + Invalid config detected Detectada configuración inválida - + Handheld controller can't be used on docked mode. Pro controller will be selected. El mando del modo portátil no puede usarse en el modo sobremesa. El mando pro será seleccionado en su lugar. - - + + Amiibo Amiibo - - + + The current amiibo has been removed El amiibo actual fue borrado - + Error Error - - + + The current game is not looking for amiibos El juego actual no está buscando amiibos - + Amiibo File (%1);; All Files (*.*) Archivo Amiibo (%1);; Todos los archivos (*.*) - + Load Amiibo Cargar Amiibo - + Error loading Amiibo data Error al cargar los datos del Amiibo - + The selected file is not a valid amiibo El archivo seleccionado no es un amiibo válido - + The selected file is already on use El archivo seleccionado se encuentra en uso - + An unknown error occurred Ha ocurrido un error desconocido - + No firmware available No hay firmware disponible - + Firmware Corrupted Firmware corrompido - + Unknown applet Applet desconocido - + Applet doesn't map to a known value. Applet no asigna a un valor conocido. - + Record not found Grabación no encontrada - + Applet not found. Please reinstall firmware. Apple no encontrado. Por favor vuelva a instalar el firmware. - + Capture Screenshot Captura de pantalla - + PNG Image (*.png) Imagen PNG (*.png) - + TAS state: Running %1/%2 Estado de TAS: Ejecutando %1/%2 - + TAS state: Recording %1 Estado de TAS: Grabando %1 - + TAS state: Idle %1/%2 Estado de TAS: Inactivo %1/%2 - + TAS State: Invalid Estado de TAS: Inválido - + &Stop Running &Parar de ejecutarse - + Stop R&ecording &Parar grabación - + Building: %n shader(s) Construyendo: %n sombreador(es)Construyendo: %n sombreador(es)Construyendo: %n sombreador(es) - + Scale: %1x %1 is the resolution scaling factor Escala: %1x - + Speed: %1% / %2% Velocidad: %1% / %2% - + Speed: %1% Velocidad: %1% - + Game: %1 FPS Juego: %1 FPS - + Frame: %1 ms Fotograma: %1 ms - + NO AA NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE VOLUMEN: SILENCIADO - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUMEN: %1% - + Derivation Components Missing Componentes de derivación ausentes - + Decryption keys are missing. Install them now? Las claves de desencriptado están ausentes. ¿Desea instalarlas ahora? - + Wayland Detected! ¡Wayland detectado! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8052,74 +8077,74 @@ Se recomienda usar X11 en su lugar. ¿Le gustaría forzar a usarlo en futuras ejecuciones? - + Use X11 Usar X11 - + Continue with Wayland Continuar con Wayland - + Don't show again No mostrar de nuevo - + Restart Required Reinicio requerido - + Restart Eden to apply the X11 backend. Reinicie Eden para aplicar el motor X11. - + Slow Lento - + Turbo Turbo - + Unlocked Desbloqueado - + Select RomFS Dump Target Seleccione el destinatario para el volcado del RomFS - + Please select which RomFS you would like to dump. Por favor seleccione que RomFS desea volcar. - + Are you sure you want to close Eden? ¿Está seguro de que quiere cerrar Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. ¿Está seguro de que desea parar la emulación? Cualquier progreso sin guardar se perderá. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/fi.ts b/dist/languages/fi.ts index 3c3ddc5354..5b075f0b0e 100644 --- a/dist/languages/fi.ts +++ b/dist/languages/fi.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. - + Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: - + The region of the console. - + Time Zone: - + The time zone of the console. - + Sound Output Mode: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) - + BC3 (Medium quality) - + Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null - + Fast - - + + Accurate - - + + Default - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3169,33 +3161,38 @@ Would you like to delete the old save data? Taustan väri: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off - + VSync Off - + Recommended - + On - + VSync On @@ -4815,102 +4812,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6922,7 +6934,7 @@ Debug Message: - + &Pause &Pysäytä @@ -7073,7 +7085,7 @@ Debug Message: - + &Start &Käynnistä @@ -7084,7 +7096,7 @@ Debug Message: - + R&ecord @@ -7175,7 +7187,7 @@ Debug Message: - + None @@ -7201,722 +7213,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7924,74 +7951,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts index fcee775250..2f2ddd1481 100644 --- a/dist/languages/fr.ts +++ b/dist/languages/fr.ts @@ -592,12 +592,22 @@ SMAA offre la meilleure qualité. FXAA peut produire une image plus stable à des résolutions plus faibles. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Mode Plein écran : - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,12 +616,12 @@ Sans bordure offre la meilleure compatibilité avec le clavier à l'écran Le mode plein écran exclusif peut offrir de meilleures performances et un meilleur support Freesync/Gsync. - + Aspect Ratio: Format : - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -620,35 +630,35 @@ La plupart des jeux ne supportent que le format 16:9, donc des modifications son Contrôle également le format des captures d’écran. - + Use persistent pipeline cache Conserver le cache du rendu graphique - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Permet de sauvegarder les shaders sur le stockage pour un chargement plus rapide lors des démarrages ultérieurs du jeu. Le désactiver est uniquement destiné au débogage. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Émulation NVDEC : - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -657,12 +667,12 @@ Elles peuvent être décodées soit par le CPU, soit par le GPU, ou pas du tout Dans la plupart des cas, le décodage GPU offre les meilleures performances. - + ASTC Decoding Method: Méthode de décodage ASTC : - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -674,12 +684,12 @@ GPU : Utiliser les shaders de calcul du GPU pour décoder les textures ASTC (rec CPU asynchrone : Utiliser le CPU pour décoder les textures ASTC à la demande. Élimine les saccades liées au décodage ASTC, mais peut provoquer des artefacts. - + ASTC Recompression Method: Méthode de recompression ASTC : - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -687,44 +697,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3 : Le format intermédiaire sera recompressé en BC1 ou BC3, ce qui économise de la VRAM mais dégrade la qualité de l’image. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Mode d'utilisation de la VRAM : - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Permet de choisir si l’émulateur doit privilégier la conservation de la mémoire ou utiliser au maximum la mémoire vidéo disponible pour les performances. Le mode agressif peut affecter les performances d’autres applications, comme les logiciels d’enregistrement. - + Skip CPU Inner Invalidation Ignorer l'invalidation interne du CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Ignore certaines invalidations de cache lors des mises à jour de la mémoire, réduisant l’utilisation du CPU et améliorant la latence. Cela peut provoquer des plantages légers. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Mode VSync : - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -735,12 +765,12 @@ Mailbox peut offrir une latence inférieure à FIFO et n’entraîne pas de déc Immediate (pas de synchronisation) affiche ce qui est disponible et peut provoquer du déchirement. - + Sync Memory Operations Synchroniser les opérations mémoire - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -749,171 +779,159 @@ Cette option corrige des problèmes dans les jeux, mais peut dégrader les perfo Les jeux Unreal Engine 4 sont souvent ceux qui bénéficient le plus de ce réglage. - + Enable asynchronous presentation (Vulkan only) Activer la présentation asynchrone (Vulkan uniquement) - + Slightly improves performance by moving presentation to a separate CPU thread. Améliore légèrement les performances en déplaçant la présentation vers un thread CPU séparé. - + Force maximum clocks (Vulkan only) Forcer la fréquence d'horloge maximale (Vulkan uniquement) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Les exécutions fonctionnent en arrière-plan en attendant les commandes graphiques pour empêcher le GPU de réduire sa vitesse de fréquence d'horloge. - + Anisotropic Filtering: Filtrage anisotropique : - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Contrôle la qualité du rendu des textures sous des angles obliques. Il est sûr de le régler à 16x sur la plupart des GPU. - + GPU Mode: Mode GPU : - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Précision du DMA : - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Activer la compilation asynchrone des shaders - + May reduce shader stutter. Peut réduire les saccades dues aux shaders. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Utiliser le cache de pipeline Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Active le cache de pipeline spécifique au fournisseur de GPU. Cette option peut améliorer considérablement le temps de chargement des shaders dans les cas où le pilote Vulkan ne stocke pas les fichiers de cache de pipeline en interne. - + Enable Compute Pipelines (Intel Vulkan Only) Activer les pipelines de calcul (Vulkan sur Intel uniquement) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -922,95 +940,84 @@ Ce réglage n’existe que pour les pilotes propriétaires Intel et peut provoqu Les pipelines de calcul sont toujours activés sur tous les autres pilotes. - + Enable Reactive Flushing Activer le Vidage Réactif - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Utilise une purge réactive au lieu d'une purge prédictive, permettant une synchronisation de la mémoire plus précise. - + Sync to framerate of video playback Synchro la fréquence d'image de la relecture du vidéo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Éxécuter le jeu à une vitesse normale pendant la relecture du vidéo, même-ci la fréquence d'image est dévérouillée. - + Barrier feedback loops Boucles de rétroaction de barrière - + Improves rendering of transparency effects in specific games. Améliore le rendu des effets de transparence dans des jeux spécifiques. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State État dynamique étendu - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Échantillonnage de shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permet au shader de fragments de s’exécuter pour chaque échantillon dans un fragment multi-échantillonné, au lieu d’une seule fois par fragment. @@ -1018,96 +1025,96 @@ Améliore la qualité graphique au prix de performances réduites. Des valeurs plus élevées améliorent la qualité mais dégradent les performances. - + RNG Seed Seed RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Contrôle la graine du générateur de nombres aléatoires. Principalement utilisé pour le speedrun. - + Device Name Nom de l'appareil - + The name of the console. Nom de la console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Date RTC personnalisée : - + This option allows to change the clock of the console. Can be used to manipulate time in games. Cette option permet de modifier l’horloge de la console. Peut être utilisée pour manipuler le temps dans les jeux. - + The number of seconds from the current unix time Nombre de secondes écoulées depuis le 1er janvier 1970. - + Language: Langue : - + This option can be overridden when region setting is auto-select Cette option peut être remplacée lorsque la région est sur auto. - + Region: Région : - + The region of the console. Région de la console. - + Time Zone: Fuseau horaire : - + The time zone of the console. Fuseau horaire de la console. - + Sound Output Mode: Mode de sortie sonore : - + Console Mode: Mode console : - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1116,59 +1123,47 @@ Les jeux adaptent leur résolution, leurs paramètres graphiques et les manettes Passer en mode Portable peut améliorer les performances sur les systèmes peu puissants. - + Prompt for user profile on boot Choisir l’utilisateur au démarrage. - + Useful if multiple people use the same PC. Utile si plusieurs personnes utilisent le même PC. - + Pause when not in focus Pause lorsque la fenêtre n’est pas active. - + Pauses emulation when focusing on other windows. Met l’émulation en pause dès que l’utilisateur change de fenêtre. - + Confirm before stopping emulation Confirmer avant d'arrêter l'émulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Ignore les demandes de confirmation pour arrêter l’émulation. L’activer permet de contourner ces confirmations et de quitter directement l’émulation. - + Hide mouse on inactivity Cacher la souris en cas d'inactivité - + Hides the mouse after 2.5s of inactivity. Cache le curseur après 2,5 secondes d’inactivité. - - - Disable controller applet - Désactiver l'applet du contrôleur - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Désactive de force le menu de configuration des manettes dans les programmes émulés. -Lorsqu’un programme tente d’ouvrir ce menu, il est immédiatement fermé. - Check for updates @@ -1200,155 +1195,156 @@ Lorsqu’un programme tente d’ouvrir ce menu, il est immédiatement fermé.Applet réel - + + + Disabled + Désactivé + + + Never Jamais - + On Load Au chargement - + Always Toujours - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Asynchrone - + Uncompressed (Best quality) Non compressé (Meilleure qualité) - + BC1 (Low quality) BC1 (Basse qualité) - + BC3 (Medium quality) BC3 (Qualité moyenne) - + Auto Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Conservateur - + Aggressive Agressif - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Nul - + Fast Rapide - - + + Accurate Précis - - + + Default Par défaut - + Unsafe (fast) Insecure (rapide) - + Safe (stable) Sûr (stable) - + Immediate - + Balanced Moyen - - - Strict - - Unsafe @@ -2125,11 +2121,6 @@ Lorsqu’un programme tente d’ouvrir ce menu, il est immédiatement fermé.High (512) Élevé (512) - - - Disabled - Désactivé - ExtendedDynamicState 1 @@ -3251,33 +3242,38 @@ Would you like to delete the old save data? Couleur de l’arrière plan : - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Désactivé - + VSync Off VSync Désactivée - + Recommended Recommandé - + On Activé - + VSync On VSync Activée @@ -4899,102 +4895,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7012,7 +7023,7 @@ Message de débogage : - + &Pause &Pause @@ -7163,7 +7174,7 @@ Message de débogage : - + &Start &Démarrer @@ -7174,7 +7185,7 @@ Message de débogage : - + R&ecord En&registrer @@ -7265,7 +7276,7 @@ Message de débogage : - + None Aucun @@ -7291,722 +7302,737 @@ Message de débogage : - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Remettre le son - + Mute Couper le son - + Reset Volume Réinitialiser le volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue &Continuer - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... Fermeture du logiciel... - + Save Data Données de sauvegarde - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? Réinitialiser le temps de jeu ? - - + + RomFS Extraction Failed! L'extraction de la RomFS a échoué ! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel Annuler - + RomFS Extraction Succeeded! Extraction de la RomFS réussi ! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties Propriétés - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game Jeu - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install Échec de l'installation - + The title type you selected for the NCA is invalid. - + File not found Fichier non trouvé - + File "%1" not found - + OK OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo Amiibo - - + + The current amiibo has been removed - + Error Erreur - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo Charger un Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted Firmware corrompu - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE VOLUME : MUET - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! Wayland détecté ! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8014,74 +8040,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/hu.ts b/dist/languages/hu.ts index 11f04195b9..29ba70527e 100644 --- a/dist/languages/hu.ts +++ b/dist/languages/hu.ts @@ -572,12 +572,22 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Teljes képernyős mód: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -586,47 +596,47 @@ A borderless (szegély nélküli) biztosítja a legjobb kompatibilitást a képe Az exkluzív teljes képernyő jobb teljesítményt és jobb Freesync/Gsync támogatást kínálhat. - + Aspect Ratio: Képarány: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Lehetővé teszi az árnyékolók tárolását a gyorsabb betöltés érdekében a következő játékindításokkor. Kikapcsolása csak hibakeresésre szolgál. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC emuláció: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -635,12 +645,12 @@ A dekódoláshoz használhatja a CPU-t vagy a GPU-t, vagy egyáltalán nem vége A legtöbb esetben a GPU dekódolás nyújtja a legjobb teljesítményt. - + ASTC Decoding Method: ASTC dekódoló módszer: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -649,55 +659,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC újraszűrési módszer: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM használati mód: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: VSync mód: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -705,426 +735,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aszinkron prezentálás engedélyezése (csak Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Kicsit javítja a teljesítményt azáltal, hogy a megjelenítést külön CPU szálra helyezi át. - + Force maximum clocks (Vulkan only) Maximális órajelek kényszerítése (csak Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. A háttérben fut, miközben várja a grafikai parancsokat, hogy a GPU ne csökkentse az órajelét. - + Anisotropic Filtering: Anizotropikus szűrés: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan pipeline gyorsítótár használata. - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Reaktív ürítés használata - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Reaktív ürítést használ a prediktív ürítés helyett, ami pontosabb memóriaszinkronizálást tesz lehetővé. - + Sync to framerate of video playback Szinkronizálás a videolejátszás képkockasebességéhez - + Run the game at normal speed during video playback, even when the framerate is unlocked. A játék futtatása normál sebességgel videolejátszás közben, még akkor is, ha a képkockasebesség fel van oldva. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. Javítja az átlátszósági effektek megjelenítését bizonyos játékokban. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Eszköznév - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Egyéni RTC dátum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Nyelv: - + This option can be overridden when region setting is auto-select - + Region: Régió: - + The region of the console. - + Time Zone: Időzóna: - + The time zone of the console. - + Sound Output Mode: Hangkimeneti mód: - + Console Mode: Konzol mód: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Emuláció leállításának megerősítése - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Egér elrejtése inaktivitáskor - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Vezérlő applet letiltása - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1156,155 +1152,156 @@ When a program attempts to open the controller applet, it is immediately closed. Valódi applet - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU aszinkron - + Uncompressed (Best quality) Tömörítetlen (legjobb minőség) - + BC1 (Low quality) BC1 (alacsony minőség) - + BC3 (Medium quality) BC3 (közepes minőség) - + Auto Automatikus - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Takarékos - + Aggressive Aggresszív - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - - + + Accurate Pontos - - + + Default Alapértelmezett - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2081,11 +2078,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3180,33 +3172,38 @@ Would you like to delete the old save data? Háttérszín: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Ki - + VSync Off VSync Ki - + Recommended Ajánlott - + On Be - + VSync On VSync Be @@ -4828,102 +4825,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6938,7 +6950,7 @@ Debug Message: - + &Pause &Szünet @@ -7089,7 +7101,7 @@ Debug Message: - + &Start &Indítás @@ -7100,7 +7112,7 @@ Debug Message: - + R&ecord F&elvétel @@ -7191,7 +7203,7 @@ Debug Message: - + None @@ -7217,722 +7229,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7940,74 +7967,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/id.ts b/dist/languages/id.ts index 30f30a8c1d..7792d12f35 100644 --- a/dist/languages/id.ts +++ b/dist/languages/id.ts @@ -583,12 +583,22 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Mode Layar Penuh: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -597,47 +607,47 @@ Borderless menawarkan kompatibilitas terbaik dengan keyboard di layar yang dimin Layar penuh eksklusif mungkin menawarkan performa yang lebih baik dan dukungan Freesync/Gsync yang lebih baik. - + Aspect Ratio: Rasio Aspek: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Memungkinkan penyimpanan shader untuk mempercepat pengambilan pada saat game berikutnya dimulai. Menonaktifkannya hanya dimaksudkan untuk debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulasi NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -646,12 +656,12 @@ Ini dapat menggunakan CPU atau GPU untuk dekode, atau tidak melakukan dekode sam Dalam kebanyakan kasus, dekode GPU memberikan kinerja terbaik. - + ASTC Decoding Method: ASTC Metode Dekoding: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -660,55 +670,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC Metode Pemampatan Ulang: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Mode Penggunaan VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation Abaikan Invalidasi Internal CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Mode Sinkronisasi Vertikal - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -716,427 +746,393 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations Sinkronisasi Operasi Memori - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aktifkan presentasi asinkron (hanya Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Meningkatkan kinerja sedikit dengan memindahkan presentasi ke thread CPU terpisah. - + Force maximum clocks (Vulkan only) Paksa jam maximum (Vulkan only) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Berjalan bekerja di latar belakang sambil menunggu perintah grafis untuk mencegah GPU agar tidak menurunkan kecepatan jamnya. - + Anisotropic Filtering: Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: Mode GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Akurasi DMA - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Gunakan kompilasi shader asinkron - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Gunakan pipeline cache Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Memungkinkan cache pipeline spesifik vendor GPU. Opsi ini dapat meningkatkan waktu pemuatan shader secara signifikan dalam kasus di mana driver Vulkan tidak menyimpan file cache pipeline secara internal. - + Enable Compute Pipelines (Intel Vulkan Only) Aktifkan Pipa Komputasi (Hanya Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Aktifkan Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Menggunakan pemadatan reaktif alih-alih pemadatan prediktif, memungkinkan sinkronisasi memori yang lebih akurat. - + Sync to framerate of video playback Sinkronkan dengan kecepatan pemutaran video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Jalankan permainan dengan kecepatan normal selama pemutaran video, bahkan ketika framerate tidak terkunci. - + Barrier feedback loops Loop umpan balik penghalang - + Improves rendering of transparency effects in specific games. Meningkatkan rendering efek transparansi dalam game tertentu. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects Perbaiki efek bloom - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Pencahayaan Sampel - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Benih RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nama Perangkat - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Tanggal RTC Kustom: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Bahasa - + This option can be overridden when region setting is auto-select - + Region: Wilayah: - + The region of the console. - + Time Zone: Zona Waktu: - + The time zone of the console. - + Sound Output Mode: Mode keluaran suara. - + Console Mode: Mode Konsol - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Konfirmasi sebelum menghentikan emulasi - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Sembunyikan mouse saat tidak aktif - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Nonaktifkan aplikasi pengontrol - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1168,155 +1164,156 @@ When a program attempts to open the controller applet, it is immediately closed. Aplikasi nyata - + + + Disabled + + + + Never Tidak Pernah - + On Load - + Always Selalu - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU sinkron - + Uncompressed (Best quality) Tidak terkompresi (Kualitas Terbaik) - + BC1 (Low quality) BC1 (Kualitas rendah) - + BC3 (Medium quality) BC3 (Kualitas sedang) - + Auto Otomatis - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Konservatif - + Aggressive Agresif - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast Cepat - - + + Accurate Akurat - - + + Default Bawaan - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced Seimbang - - - Strict - - Unsafe @@ -2093,11 +2090,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) Tinggi (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3218,33 +3210,38 @@ Would you like to delete the old save data? Warna Latar: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Mati - + VSync Off VSync Mati - + Recommended Direkomendasikan - + On Nyala - + VSync On VSync Aktif @@ -4865,102 +4862,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6972,7 +6984,7 @@ Debug Message: - + &Pause &Jeda @@ -7123,7 +7135,7 @@ Debug Message: - + &Start &Mulai @@ -7134,7 +7146,7 @@ Debug Message: - + R&ecord R&ekam @@ -7225,7 +7237,7 @@ Debug Message: - + None @@ -7251,722 +7263,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7974,74 +8001,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/it.ts b/dist/languages/it.ts index bb485065c8..2cf9191129 100644 --- a/dist/languages/it.ts +++ b/dist/languages/it.ts @@ -592,12 +592,22 @@ SMAA offre la migliore qualità. FXAA può produrre un'immagine più stabile nelle risoluzioni più basse. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Modalità schermo intero: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,12 +616,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp "Schermo intero esclusivo" può offrire prestazioni maggiori e un supporto migliore al Freesync/Gsync. - + Aspect Ratio: Rapporto d'aspetto: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -620,36 +630,36 @@ La maggior parte dei giochi supporta solo 16:9, quindi le mod sono necessarie pe Controlla anche il rapporto d'aspetto degli screenshot. - + Use persistent pipeline cache Usa la cache persistente delle pipeline - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Permette di salvare gli shader su disco per velocizzarne il caricamento negli avvii successivi del gioco. Disabilitarla ha senso solo quando si effettua il debug. - + Use asynchronous GPU emulation Abilita l'emulazione asincrona della GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Utilizza un thread aggiuntivo della CPU per il rendering. Quest'opzione dovrebbe sempre rimanere abilitata. - + NVDEC emulation: Emulazione NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -658,12 +668,12 @@ In most cases, GPU decoding provides the best performance. Nella maggior parte dei casi, la decodifica tramite GPU fornisce prestazioni migliori. - + ASTC Decoding Method: Metodo di decodifica ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -675,12 +685,12 @@ GPU: Usa i compute shader della GPU per decodificare le texture ASTC (consigliat CPU (Asincrono): Usa la CPU per decodificare le texture ASTC se richiesto. Elimina lo stuttering causato dalla decodifica ma potrebbe generare artefatti visivi. - + ASTC Recompression Method: Metodo di ricompressione ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -688,44 +698,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: Il formato intermedio sarà ricompresso nel formato BC1 o BC3, risparmiando VRAM ma peggiorando la qualità visiva. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Modalità di utilizzo della VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Determina se l'emulatore dovrebbe risparmiare memoria o utilizzarne il più possibile per massimizzare le prestazioni. La modalità aggressiva potrebbe impattare sulle prestazioni di altre applicazioni, come i programmi di registrazione. - + Skip CPU Inner Invalidation Salta invalidamento interno CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Salta alcuni invalidamenti della cache durante gli aggiornamenti della memoria, riducendo l'uso della CPU e migliorando la latenza. Questo potrebbe causare soft-crash. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Modalità VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -736,12 +766,12 @@ Mailbox può avere una latenza minore di FIFO e non ha tearing ma potrebbe perde Immediate (no sincronizzazione) mostra tutto il disponibile, quindi anche tearing. - + Sync Memory Operations Sincronizza operazioni di memoria - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -750,171 +780,159 @@ Questa opzione dovrebbe risolvere problemi in alcuni giochi, ma potrebbe ridurre I giochi con Unreal Engine 4 sembrano essere i più colpiti. - + Enable asynchronous presentation (Vulkan only) Abilita la presentazione asincrona (solo Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Migliora di poco le prestazioni spostando la presentazione su un thread della CPU separato. - + Force maximum clocks (Vulkan only) Forza clock massimi (solo Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Esegue del lavoro in background durante l'attesa dei comandi grafici per evitare che la GPU diminuisca la sua velocità di clock. - + Anisotropic Filtering: Filtro anisotropico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controlla la qualità del rendering delle texture negli angoli obliqui. Si può utilizzare in modo sicuro al 16x su quasi tutte le GPU. - + GPU Mode: Modalità GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Precisione DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Abilita la compilazione asincrona degli shader - + May reduce shader stutter. Può ridurre i fenomeni di stuttering (scatti) causati dagli shader. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Usa la cache delle pipeline di Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Abilita la cache delle pipeline specifica del produttore della GPU. Quest'opzione può ridurre di molto i tempi di caricamento degli shader nei casi in cui il driver Vulkan non memorizza la cache delle pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Abilita le compute pipeline (solo per Vulkan su Intel) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -923,192 +941,181 @@ Quest'impostazione esiste solo per i driver proprietari Intel e potrebbe fa Le compute pipelines sono sempre attivate su tutti gli altri drivers. - + Enable Reactive Flushing Abilita il flushing reattivo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Utilizza il flushing reattivo invece di quello predittivo, al fine di ottenere una sincronizzazione della memoria più accurata. - + Sync to framerate of video playback Sincronizza il framerate a quello del video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Esegue il gioco a velocità normale durante le cutscene, anche quando il framerate è sbloccato. - + Barrier feedback loops Barrier feedback loops - + Improves rendering of transparency effects in specific games. Migliora il rendering degli effetti di trasparenza in alcuni giochi. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State Stato dinamico esteso - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controlla il numero di funzionalità che possono essere usate con lo stato dinamico esteso. Gli stati più alti consentono più funzionalità e possono migliorare le prestazioni, ma possono causare ulteriori problemi grafici. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading Sample shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permette al fragment shader di eseguire per campione in un frammento multi-campione invece che una volta per frammento. Migliora la qualità grafica a scapito delle prestazioni. Alti valori migliorano la qualità ma peggiorano le prestazioni. - + RNG Seed Seed RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Controlla il seme del generatore di numeri casuali. Principalmente utilizzato per le speedrun. - + Device Name Nome del dispositivo - + The name of the console. Il nome della console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Data RTC personalizzata: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Quest'opzione permette di modificare l'orologio della console. Può essere usato per manipolare il tempo nei giochi. - + The number of seconds from the current unix time Il numero di secondi dal tempo Unix attuale - + Language: Lingua: - + This option can be overridden when region setting is auto-select Può essere rimpiazzato se il fuso orario della Regione è impostato su Auto - + Region: Regione: - + The region of the console. La regione della console. - + Time Zone: Fuso orario: - + The time zone of the console. Il fuso orario della console. - + Sound Output Mode: Modalità di output del suono: - + Console Mode: Modalità console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1117,59 +1124,47 @@ I giochi ne terranno conto e modificheranno la risoluzione, i dettagli e i contr Impostare l'opzione su "Portatile" può aiutare a migliorare le prestazioni sui sistemi meno potenti. - + Prompt for user profile on boot Scegli il profilo utente all'avvio - + Useful if multiple people use the same PC. Utile se più persone utilizzano lo stesso PC. - + Pause when not in focus Metti in pausa quando la finestra non è in primo piano - + Pauses emulation when focusing on other windows. Mette in pausa l'emulazione quando altre finestre sono in primo piano. - + Confirm before stopping emulation Chiedi conferma prima di arrestare l'emulazione - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Sovrascrive le richieste di conferma per fermare l'emulazione. Abilitarla bypassa queste richieste e l'emulazione cesserà immediatamente. - + Hide mouse on inactivity Nascondi il puntatore del mouse se inattivo - + Hides the mouse after 2.5s of inactivity. Nasconde il mouse dopo 2,5 secondi di inattività. - - - Disable controller applet - Disabilita l'applet controller - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Disabilita forzatamente l'uso dell'applet del controller nei programmi emulati. -Quando un programma proverà ad aprire l'applet del controller, quest'ultimo verrà immediatamente chiuso. - Check for updates @@ -1201,155 +1196,156 @@ Quando un programma proverà ad aprire l'applet del controller, quest' Applet reale - + + + Disabled + Disabilitato + + + Never Mai - + On Load Al caricamento - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU (Asincrono) - + Uncompressed (Best quality) Nessuna compressione (qualità migliore) - + BC1 (Low quality) BC1 (qualità bassa) - + BC3 (Medium quality) BC3 (qualità media) - + Auto Automatico - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservativa - + Aggressive Aggressiva - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (shader assembly, solo NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (sperimentale, solo AMD/MESA) - + Null Nullo - + Fast Veloce - - + + Accurate Accurata - - + + Default Predefinito - + Unsafe (fast) Non sicuro (veloce) - + Safe (stable) Sicuro (stabile) - + Immediate - + Balanced Bilanciata - - - Strict - - Unsafe @@ -2126,11 +2122,6 @@ Quando un programma proverà ad aprire l'applet del controller, quest' High (512) Alto (512) - - - Disabled - Disabilitato - ExtendedDynamicState 1 @@ -3262,33 +3253,38 @@ Vuoi cancellare i dati precedenti? Colore dello sfondo: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Disattivato - + VSync Off VSync disattivato - + Recommended Consigliata - + On Attivato - + VSync On VSync attivato @@ -4919,102 +4915,117 @@ Una volta rimosse, non potranno più essere recuperate. Sei assolutamente sicuro - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7032,7 +7043,7 @@ Messaggio debug: - + &Pause &Pausa @@ -7183,7 +7194,7 @@ Messaggio debug: - + &Start &Avvia @@ -7194,7 +7205,7 @@ Messaggio debug: - + R&ecord R&egistra @@ -7285,7 +7296,7 @@ Messaggio debug: - + None Nessuno @@ -7311,400 +7322,405 @@ Messaggio debug: - + Small (32x32) Piccola (32x32) - + Standard (64x64) Normale (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Intera (256x256) - + Broken Vulkan Installation Detected Rilevata installazione di Vulkan non funzionante - + Vulkan initialization failed during boot. L'inizializzazione di Vulkan è fallita durante l'avvio. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Gioco in esecuzione - + Loading Web Applet... Caricamento dell'applet web... - - + + Disable Web Applet Disabilita l'applet web - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Disabilitare l'applet web potrebbe causare dei comportamenti indesiderati e andrebbe fatto solo con Super Mario 3D All-Stars. Sei sicuro di voler procedere? (Puoi riabilitarlo quando vuoi nelle impostazioni di debug.) - + The amount of shaders currently being built Il numero di shader in fase di compilazione - + The current selected resolution scaling multiplier. Il moltiplicatore attuale della risoluzione. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocità attuale dell'emulazione. Valori più alti o più bassi di 100% indicano che l'emulazione sta funzionando più velocemente o lentamente rispetto a una Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Il numero di fotogrammi al secondo che il gioco sta attualmente renderizzando. Può variare in base al gioco e alla situazione. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tempo necessario per emulare un fotogramma della Switch, senza tenere conto del limite al framerate o del V-Sync. Per un'emulazione alla massima velocità, il valore non dovrebbe essere superiore a 16.67 ms. - + Unmute Riattiva - + Mute Silenzia - + Reset Volume Reimposta volume - + + Configure Effects... + + + + &Clear Recent Files &Cancella i file recenti - + &Continue &Continua - + Warning: Outdated Game Format Attenzione: Formato del gioco obsoleto - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Stai usando una cartella contenente una ROM decostruita per avviare questo gioco, che è un formato obsoleto e sostituito da NCA, NAX, XCI o NSP. Le ROM decostruite non hanno icone né metadati e non supportano gli aggiornamenti.<br>Per una spiegazione sui vari formati della console Switch supportati da Eden, consulta il manuale utente. Non riceverai di nuovo questo avviso. - - + + Error while loading ROM! Errore nel caricamento della ROM! - + The ROM format is not supported. Il formato della ROM non è supportato. - + An error occurred initializing the video core. Errore durante l'inizializzazione del componente video di base - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Errore nel caricamento della ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Esegui un nuovo dump dei tuoi file o chiedi aiuto su Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Si è verificato un errore sconosciuto. Consulta il log per maggiori dettagli. - - + + (64-bit) (64 bit) - - + + (32-bit) (32 bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Chiusura del software in corso... - + Save Data Dati di salvataggio - + Mod Data Dati delle mod - + Error Opening %1 Folder Impossibile aprire la cartella %1 - - + + Folder does not exist! La cartella non esiste! - + Remove Installed Game Contents? Rimuovere il contenuto del gioco installato? - + Remove Installed Game Update? Rimuovere l'aggiornamento installato? - + Remove Installed Game DLC? Rimuovere il DLC installato? - + Remove Entry Rimuovi voce - + Delete OpenGL Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader OpenGL? - + Delete Vulkan Transferable Shader Cache? Vuoi rimuovere la cache trasferibile degli shader Vulkan? - + Delete All Transferable Shader Caches? Vuoi rimuovere tutte le cache trasferibili degli shader? - + Remove Custom Game Configuration? Rimuovere la configurazione personalizzata del gioco? - + Remove Cache Storage? Rimuovere la cache del gioco? - + Remove File Rimuovi file - + Remove Play Time Data Reimposta il tempo di gioco - + Reset play time? Vuoi reimpostare il tempo di gioco? - - + + RomFS Extraction Failed! Estrazione RomFS fallita! - + There was an error copying the RomFS files or the user cancelled the operation. Si è verificato un errore durante la copia dei file del RomFS o l'operazione è stata annullata dall'utente. - + Full Completa - + Skeleton Cartelle - + Select RomFS Dump Mode Seleziona la modalità di estrazione del RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Seleziona come vorresti estrarre il RomFS. <br>La modalità Completa copierà tutti i file in una nuova cartella mentre<br>la modalità Cartelle creerà solamente la struttura delle cartelle. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Non c'è abbastanza spazio disponibile in %1 per estrarre il RomFS. Libera lo spazio o seleziona una cartella di estrazione diversa in Emulazione > Configura > Sistema > File system > Cartella di estrazione - + Extracting RomFS... Estrazione RomFS in corso... - - + + Cancel Annulla - + RomFS Extraction Succeeded! Estrazione RomFS riuscita! - + The operation completed successfully. L'operazione è stata completata con successo. - + Error Opening %1 Impossibile aprire %1 - + Select Directory Seleziona cartella - + Properties Proprietà - + The game properties could not be loaded. Non è stato possibile caricare le proprietà del gioco. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Eseguibile Switch (%1);;Tutti i file (*.*) - + Load File Carica file - + Open Extracted ROM Directory Apri la cartella della ROM estratta - + Invalid Directory Selected Cartella selezionata non valida - + The directory you have selected does not contain a 'main' file. La cartella che hai selezionato non contiene un file "main". - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) File Switch installabili (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installa file - + %n file(s) remaining %n file rimanente%n di file rimanenti%n file rimanenti - + Installing file "%1"... Installazione del file "%1"... + - Install Results Risultati dell'installazione - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Per evitare possibli conflitti, sconsigliamo di installare i giochi base su NAND. Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were newly installed %n nuovo file è stato installato @@ -7713,7 +7729,7 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) were overwritten %n file è stato sovrascritto @@ -7722,7 +7738,7 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + %n file(s) failed to install %n file non è stato installato a causa di errori @@ -7731,314 +7747,324 @@ Usa questa funzione solo per installare aggiornamenti e DLC. - + System Application Applicazione di sistema - + System Archive Archivio di sistema - + System Application Update Aggiornamento di un'applicazione di sistema - + Firmware Package (Type A) Pacchetto firmware (tipo A) - + Firmware Package (Type B) Pacchetto firmware (tipo B) - + Game Gioco - + Game Update Aggiornamento di gioco - + Game DLC DLC - + Delta Title Titolo delta - + Select NCA Install Type... Seleziona il tipo di installazione NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Seleziona il tipo del file NCA da installare: (Nella maggior parte dei casi, il valore predefinito "Gioco" va bene) - + Failed to Install Installazione fallita - + The title type you selected for the NCA is invalid. Il tipo che hai selezionato per il file NCA non è valido. - + File not found File non trovato - + File "%1" not found File "%1" non trovato - + OK OK - + Function Disabled Funzionalità disabilitata - + Compatibility list reporting is currently disabled. Check back later! La segnalazione della compatibilità è al momento disabilitata. Torna a controllare più avanti! - + Error opening URL Impossibile aprire l'URL - + Unable to open the URL "%1". Non è stato possibile aprire l'URL "%1". - + TAS Recording Registrazione TAS - + Overwrite file of player 1? Vuoi sovrascrivere il file del giocatore 1? - + Invalid config detected Rilevata configurazione non valida - + Handheld controller can't be used on docked mode. Pro controller will be selected. Il controller portatile non può essere utilizzato in modalità dock. Verrà selezionato il controller Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed L'Amiibo corrente è stato rimosso - + Error Errore - - + + The current game is not looking for amiibos Il gioco in uso non è alla ricerca di Amiibo - + Amiibo File (%1);; All Files (*.*) File Amiibo (%1);; Tutti i file (*.*) - + Load Amiibo Carica Amiibo - + Error loading Amiibo data Impossibile caricare i dati dell'Amiibo - + The selected file is not a valid amiibo Il file selezionato non è un Amiibo valido - + The selected file is already on use Il file selezionato è già in uso - + An unknown error occurred Si è verificato un errore sconosciuto - + No firmware available Nessun firmware disponibile - + Firmware Corrupted Firmware corrotto - + Unknown applet Applet sconosciuto - + Applet doesn't map to a known value. L'applet non è associato a un valore noto. - + Record not found Non trovato - + Applet not found. Please reinstall firmware. Applet non trovato. Reinstalla il firmware. - + Capture Screenshot Cattura screenshot - + PNG Image (*.png) Immagine PNG (*.png) - + TAS state: Running %1/%2 Stato TAS: In esecuzione (%1/%2) - + TAS state: Recording %1 Stato TAS: Registrazione in corso (%1) - + TAS state: Idle %1/%2 Stato TAS: In attesa (%1/%2) - + TAS State: Invalid Stato TAS: Non valido - + &Stop Running &Interrompi esecuzione - + Stop R&ecording Interrompi r&egistrazione - + Building: %n shader(s) Compilazione di %n shaderCompilazione di %n shaderCompilazione di %n shader - + Scale: %1x %1 is the resolution scaling factor Risoluzione: %1x - + Speed: %1% / %2% Velocità: %1% / %2% - + Speed: %1% Velocità: %1% - + Game: %1 FPS Gioco: %1 FPS - + Frame: %1 ms Frame: %1 ms - + NO AA NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE VOLUME: MUTO - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUME: %1% - + Derivation Components Missing Componenti di derivazione mancanti - + Decryption keys are missing. Install them now? Chiavi di crittografia mancanti. Vuoi installarle ora? - + Wayland Detected! Wayland rilevato! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8049,74 +8075,74 @@ Si consiglia invece di usare X11. Vuoi forzare l'uso di quest'ultimo per i prossimi avvii? - + Use X11 Usa X11 - + Continue with Wayland Continua con Wayland - + Don't show again Non mostrare di nuovo - + Restart Required Riavvio richiesto - + Restart Eden to apply the X11 backend. Riavvia Eden per usare il back-end X11. - + Slow Lento - + Turbo Turbo - + Unlocked Sbloccato - + Select RomFS Dump Target Seleziona RomFS da estrarre - + Please select which RomFS you would like to dump. Seleziona quale RomFS vorresti estrarre. - + Are you sure you want to close Eden? Sei sicuro di voler uscire da Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Sei sicuro di voler arrestare l'emulazione? Tutti i progressi non salvati verranno perduti. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts index 01de70d2b2..2341618395 100644 --- a/dist/languages/ja_JP.ts +++ b/dist/languages/ja_JP.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: フルスクリーンモード: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: アスペクト比: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC エミュレーション: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: ASTC デコード方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC 再圧縮方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: 垂直同期: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -702,426 +732,392 @@ Mailbox は FIFO よりも遅延が小さくティアリングがありません Immediate (no synchronization) は表示可能なものをすべて表示し, ティアリング発生の可能性があります. - + Sync Memory Operations メモリ操作の同期 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) 非同期プレゼンテーション (Vulkan のみ) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) 最大クロック強制 (Vulkan のみ) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. GPUのクロックスピードを下げないように、グラフィックコマンドを待っている間、バックグラウンドで作業を実行させます。 - + Anisotropic Filtering: 異方性フィルタリング: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: GPUモード: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: DMA精度: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan パイプラインキャッシュを使用 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) コンピュート・パイプラインの有効化(インテル Vulkan のみ) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback ビデオ再生のフレームレートに同期する - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. 特定のゲームにおける透明エフェクトのレンダリングを改善します。 - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed 乱数シード値の変更 - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name デバイス名 - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: 言語: - + This option can be overridden when region setting is auto-select - + Region: 地域: - + The region of the console. - + Time Zone: タイムゾーン: - + The time zone of the console. - + Sound Output Mode: 音声出力モード: - + Console Mode: コンソールモード: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation エミュレーションを停止する前に確認する - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity 非アクティブ時にマウスカーソルを隠す - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - コントローラーアプレットの無効化 - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1153,155 +1149,156 @@ When a program attempts to open the controller applet, it is immediately closed. - + + + Disabled + 無効 + + + Never - + On Load - + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 非同期 - + Uncompressed (Best quality) 圧縮しない (最高品質) - + BC1 (Low quality) BC1 (低品質) - + BC3 (Medium quality) BC3 (中品質) - + Auto 自動 - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (アセンブリシェーダー、NVIDIA のみ) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V(実験的、AMD/Mesaのみ) - + Null - + Fast - - + + Accurate 正確 - - + + Default デフォルト - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2078,11 +2075,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - 無効 - ExtendedDynamicState 1 @@ -3204,33 +3196,38 @@ Would you like to delete the old save data? 背景色: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off オフ - + VSync Off VSync オフ - + Recommended 推奨 - + On オン - + VSync On VSync オン @@ -4851,102 +4848,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6961,7 +6973,7 @@ Debug Message: - + &Pause 中断(&P) @@ -7112,7 +7124,7 @@ Debug Message: - + &Start 実行(&S) @@ -7123,7 +7135,7 @@ Debug Message: - + R&ecord 記録(&R) @@ -7214,7 +7226,7 @@ Debug Message: - + None @@ -7240,722 +7252,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7963,74 +7990,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/ko_KR.ts b/dist/languages/ko_KR.ts index ecb5a29660..c78a853a20 100644 --- a/dist/languages/ko_KR.ts +++ b/dist/languages/ko_KR.ts @@ -594,12 +594,22 @@ SMAA는 최고의 화질을 제공합니다. FXAA는 저해상도에서 더 안정적인 화면을 구현할 수 있습니다. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: 전체 화면 모드: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -608,12 +618,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp 전체 화면 전용 모드는 더 나은 성능과 FreeSync/GSync 지원을 제공할 수 있습니다. - + Aspect Ratio: 화면 비율: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -622,36 +632,36 @@ Also controls the aspect ratio of captured screenshots. 또한 캡처된 스크린샷의 화면 비율도 제어합니다. - + Use persistent pipeline cache 지속적 파이프라인 캐시 사용 - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. 셰이더를 저장소에 저장하여 다음 게임 실행 시 더 빠르게 불러올 수 있도록 합니다. 이 옵션을 비활성화하는 것은 디버깅 목적으로만 사용하세요. - + Use asynchronous GPU emulation 비동기 GPU 에뮬레이션 사용 - + Uses an extra CPU thread for rendering. This option should always remain enabled. 렌더링에 추가 CPU 스레드를 사용합니다. 이 옵션은 항상 활성화된 상태로 유지해야 합니다. - + NVDEC emulation: NVDEC 에뮬레이션: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -660,12 +670,12 @@ In most cases, GPU decoding provides the best performance. 대부분의 경우 GPU 디코딩이 가장 좋은 성능을 제공합니다. - + ASTC Decoding Method: ASTC 디코딩 방식: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -677,12 +687,12 @@ GPU: GPU의 컴퓨트 셰이더를 사용하여 ASTC 텍스처를 디코딩합 CPU 비동기: 필요에 따라 CPU를 사용하여 ASTC 텍스처를 디코딩합니다. ASTC 디코딩으로 인한 끊김 현상이 없어지지만, 아티팩트가 발생할 수 있습니다. - + ASTC Recompression Method: ASTC 재압축 방식: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -691,44 +701,64 @@ BC1/BC3: 중간 형식은 BC1 또는 BC3 형식으로 재압축되어 VRAM을 절약하지만 이미지 품질이 저하됩니다. - + Frame Pacing Mode (Vulkan only) 프레임 페이싱 모드(Vulkan 전용) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. 에뮬레이터가 프레임 페이싱을 관리하는 방식을 제어하여 끊김 현상을 줄이고 프레임 속도를 더욱 부드럽고 일관되게 만듭니다. - + VRAM Usage Mode: VRAM 사용 모드: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. 에뮬레이터가 메모리를 절약할지, 아니면 사용 가능한 비디오 메모리를 최대한 활용하여 성능을 향상시킬지 선택합니다. 적극적 모드는 녹화 소프트웨어와 같은 다른 애플리케이션의 성능에 영향을 줄 수 있습니다. - + Skip CPU Inner Invalidation CPU 내부 무효화 건너뛰기 - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. 메모리 업데이트 중 특정 캐시 무효화를 건너뛰어 CPU 사용량을 줄이고 지연 시간을 개선합니다. 하지만 이로 인해 소프트 크래시가 발생할 수 있습니다. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: 수직 동기화 모드: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -739,12 +769,12 @@ Mailbox는 FIFO보다 지연 시간이 짧고 화면 찢김 현상이 없지만 즉각적(동기화 없음)는 사용 가능한 모든 화면을 표시하며 화면 찢김 현상이 발생할 수 있습니다. - + Sync Memory Operations 메모리 작업 동기화 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -753,44 +783,44 @@ Unreal Engine 4 games often see the most significant changes thereof. 대개 언리얼 엔진 4 게임에서 이러한 변경 사항이 가장 두드러지게 나타납니다. - + Enable asynchronous presentation (Vulkan only) 비동기 프레젠테이션 활성화(Vulkan 전용) - + Slightly improves performance by moving presentation to a separate CPU thread. 프레젠테이션을 별도의 CPU 스레드로 이동시켜 성능을 약간 향상시킵니다. - + Force maximum clocks (Vulkan only) 최대 클럭 강제 적용(Vulkan 전용) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. 그래픽 명령을 기다리는 동안 백그라운드에서 작업을 실행하여 GPU 클럭 속도가 저하되는 것을 방지합니다. - + Anisotropic Filtering: 비등방성 필터링: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. 비스듬한 각도에서의 텍스처 렌더링 품질을 제어합니다. 대부분의 GPU에서 16x로 설정해도 안전합니다. - + GPU Mode: GPU 모드: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -799,12 +829,12 @@ Particles tend to only render correctly with Accurate mode. 특히 조각들은 정확 모드에서만 제대로 렌더링되는 경향이 있습니다. - + DMA Accuracy: DMA 정확도: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -813,76 +843,58 @@ Default follows the GPU Accuracy setting. 기본값은 GPU 정확도 설정을 따릅니다. - + GPU Fence Behavior: GPU 펜스 동작: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. - GPU 펜스 동기화 동작을 제어합니다. -즉시는 가장 빠른 옵션이지만 일부 문제가 발생할 수 있습니다. -균형은 호환성이 더 좋고 일부 게임에서 발생하는 문제를 해결할 수 있습니다. -정확은 성능 저하를 감수하고 호환성을 더욱 향상시킵니다. -엄격은 가장 느린 옵션이지만 더 엄격한 동기화가 필요한 문제를 해결할 수 있습니다. -기본값은 GPU 정확도 설정을 따릅니다. +Default follows the GPU Mode setting. + - - Enable GPU buffer readback - GPU 버퍼 되읽기 활성화 - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - 수정된 GPU 데이터를 업로드하기 전에 다시 읽어들여 보존합니다. -일부 게임에서는 특정 효과를 제대로 렌더링하기 위해 이 기능이 필요합니다. - - - + Enable asynchronous shader compilation 비동기 셰이더 컴파일 활성화 - + May reduce shader stutter. 셰이더 끊김 현상을 줄일 수 있습니다. - + GPU Clocks GPU 클럭 - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. 게임이 GPU 작업이 실제보다 더 빨리 완료된다고 착각하게 만들어, 스위치의 클럭 속도에 맞추기 위해 해상도와 렌더 거리를 낮추는 작업을 중단하게 합니다. - + GPU Unswizzle GPU 언스위즐 - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. GPU 컴퓨팅을 사용하여 BCn 3D 텍스처 디코딩을 가속화합니다. 충돌이나 그래픽 결함이 발생하는 경우 이 기능을 비활성화하세요. - + GPU Unswizzle Max Texture Size GPU 언스위즐 최대 텍스처 크기 - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -891,48 +903,48 @@ GPU는 중간 및 큰 크기의 텍스처에서 더 빠르지만, 매우 작은 이 값을 조정하여 GPU 가속과 CPU 오버헤드 간의 균형을 찾으세요. - + GPU Unswizzle Stream Size GPU 언스위즐 스트림 크기 - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. 프레임당 처리할 최대 텍스처 데이터 양(MiB 단위)을 설정합니다. 값이 높을수록 텍스처 로딩 중 끊김 현상을 줄일 수 있지만 프레임 일관성에 영향을 미칠 수 있습니다. - + GPU Unswizzle Chunk Size GPU 언스위즐 청크 크기 - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. 단일 디스패치에서 처리할 깊이 슬라이스 수를 결정합니다. 이 값을 높이면 고성능 GPU에서 처리량이 향상될 수 있지만, 성능이 낮은 하드웨어에서는 TDR 오류나 드라이버 시간 초과가 발생할 수 있습니다. - + Use Vulkan pipeline cache Vulkan 파이프라인 캐시 사용 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. GPU 제조사별 파이프라인 캐시를 활성화합니다. 이 옵션은 Vulkan 드라이버가 파이프라인 캐시 파일을 내부에 저장하지 않는 경우 셰이더 로딩 시간을 크게 향상시킬 수 있습니다. - + Enable Compute Pipelines (Intel Vulkan Only) 컴퓨트 파이프라인 활성화(인텔 Vulkan 전용) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -941,194 +953,182 @@ Compute pipelines are always enabled on all other drivers. 다른 모든 드라이버에서는 컴퓨트 파이프라인이 항상 활성화됩니다. - + Enable Reactive Flushing 반응형 플러싱 활성화 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. 예측형 플러싱 대신 반응형 플러싱을 사용하여 보다 정확한 메모리 동기화를 가능하게 합니다. - + Sync to framerate of video playback 비디오 재생 프레임 속도에 동기화 - + Run the game at normal speed during video playback, even when the framerate is unlocked. 프레임률 제한이 해제된 경우에도 비디오 재생 중에는 게임을 정상 속도로 실행합니다. - + Barrier feedback loops 피드백 루프 차단 - + Improves rendering of transparency effects in specific games. 특정 게임에서 투명 효과 렌더링을 개선합니다. - + Enable buffer history 버퍼 기록 활성화 - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. 이전 버퍼 상태에 접근할 수 있도록 합니다. 이 옵션은 일부 게임에서 렌더링 품질과 성능 일관성을 향상시킬 수 있습니다. - + Fix bloom effects 블룸 효과 수정 - + Removes bloom in Burnout. Burnout에서의 블룸 현상을 제거합니다. - - Enable Legacy Rescale Pass - 기존 크기 조정 무시 활성화 - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - 이전 구현의 동작을 활용하여 일부 게임에서 발생하는 크기 조정 문제를 해결할 수 있습니다. -루이지 맨션 3에서 발생했던 AMD 및 인텔 GPU의 선 아티팩트 현상과 Nvidia GPU의 회색 텍스처 깜빡임 현상을 해결하는 기존 반응에 대한 예비 해결책입니다. - - - + Extended Dynamic State 확장된 동적 상태 - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. 확장된 동적 상태에서 사용할 수 있는 기능의 수를 제어합니다. 상태 수가 많을수록 더 많은 기능을 사용할 수 있고 성능이 향상될 수 있지만, 추가적인 그래픽 문제가 발생할 수 있습니다. - + Vertex Input Dynamic State 정점 입력 동적 상태 - + Enables vertex input dynamic state feature for better quality and performance. 정점 입력의 동적 상태 기능을 활성화하여 품질과 성능을 향상시킵니다. - + Sample Shading 샘플 셰이딩 - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. 조각 셰이더가 다중샘플 조각에서 한 번당 한 조각이 아닌 샘플별로 실행될 수 있도록 합니다. 성능 저하를 감수하고 그래픽 품질을 향상시킵니다. 값이 높을수록 품질은 향상되지만 성능은 저하됩니다. - + RNG Seed RNG 시드 - + Controls the seed of the random number generator. Mainly used for speedrunning. 난수 생성기의 시드 값을 제어합니다. 주로 스피드런에 사용됩니다. - + Device Name 장치 이름 - + The name of the console. 콘솔의 이름 - + Homebrew Args 홈브루 인수 - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). 홈브루 실행 시 전달되는 명령줄 인수(예: -noglsl). - + Custom RTC Date: 사용자 지정 RTC 날짜: - + This option allows to change the clock of the console. Can be used to manipulate time in games. 이 옵션을 사용하면 콘솔의 시간을 변경할 수 있습니다. 게임 내 시간을 조작하는 데 사용할 수 있습니다. - + The number of seconds from the current unix time 현재 unix 시간으로부터 경과한 초 수 - + Language: 언어: - + This option can be overridden when region setting is auto-select 이 옵션은 지역 설정이 자동 선택으로 되어 있을 경우 번복될 수 있습니다. - + Region: 지역: - + The region of the console. 콘솔의 지역 - + Time Zone: 시간대: - + The time zone of the console. 콘솔의 시간대 - + Sound Output Mode: 소리 출력 모드: - + Console Mode: 콘솔 모드: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1137,59 +1137,47 @@ Setting to Handheld can help improve performance for low end systems. 휴대 모드로 설정하면 저사양 시스템의 성능을 향상시키는 데 도움이 될 수 있습니다. - + Prompt for user profile on boot 부팅 시 유저 프로필 입력 요청 - + Useful if multiple people use the same PC. 여러 사람이 같은 PC를 사용할 때 유용합니다. - + Pause when not in focus 초점이 맞지 않을 때 일시 중지 - + Pauses emulation when focusing on other windows. 다른 창에 초점을 맞출 때 에뮬레이션을 일시 중지합니다. - + Confirm before stopping emulation 에뮬레이션을 중지하기 전에 확인 - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. 에뮬레이션 중지를 확인하는 메시지를 무시합니다. 이 옵션을 활성화하면 이러한 메시지를 건너뛰고 에뮬레이션을 바로 종료합니다. - + Hide mouse on inactivity 마우스 커서가 비활동 상태일 때 숨기기 - + Hides the mouse after 2.5s of inactivity. 마우스 커서가 2.5초 동안 활동이 없으면 숨깁니다. - - - Disable controller applet - 컨트롤러 애플릿 비활성화 - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - 에뮬레이트된 프로그램에서 컨트롤러 애플릿 사용을 강제적으로 비활성화합니다. -프로그램이 컨트롤러 애플릿을 열려고 하면 즉시 닫힙니다. - Check for updates @@ -1221,155 +1209,156 @@ When a program attempts to open the controller applet, it is immediately closed. 실제 애플릿 - + + + Disabled + 비활성화됨 + + + Never 안 함 - + On Load 로드 시 - + Always 항상 - + CPU CPU - + GPU GPU - + CPU Asynchronous 비동기 CPU - + Uncompressed (Best quality) 비압축(최고 품질) - + BC1 (Low quality) BC1(저품질) - + BC3 (Medium quality) BC3(중간 품질) - + Auto 자동 - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative 소극적 - + Aggressive 적극적 - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM(어셈블리 셰이더, NVIDIA 전용) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V(실험적, AMD/Mesa 전용) - + Null Null - + Fast 고속 - - + + Accurate 정확 - - + + Default 기본값 - + Unsafe (fast) 불안전(빠름) - + Safe (stable) 안전(안정적) - + Immediate 즉시 - + Balanced 균형 - - - Strict - 염격 - Unsafe @@ -2146,11 +2135,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) 빠름(512) - - - Disabled - 비활성화됨 - ExtendedDynamicState 1 @@ -3284,33 +3268,38 @@ Would you like to delete the old save data? 배경색: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + + + + Off 끔 - + VSync Off 수직 동기화 끔 - + Recommended 권장 - + On 켬 - + VSync On 수직 동기화 켬 @@ -4936,102 +4925,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7049,7 +7053,7 @@ Debug Message: - + &Pause 일시중지(&P) @@ -7200,7 +7204,7 @@ Debug Message: - + &Start 시작(&S) @@ -7211,7 +7215,7 @@ Debug Message: - + R&ecord 기록(&e) @@ -7302,7 +7306,7 @@ Debug Message: - + None 없음 @@ -7328,728 +7332,743 @@ Debug Message: 캐러셀 보기(&C) - + Small (32x32) 작은 크기(32x32) - + Standard (64x64) 기본 크기(64x64) - + Large (128x128) 큰 크기(128x128) - + Full Size (256x256) 전체 크기(256x256) - + Broken Vulkan Installation Detected 손상된 Vulkan 설치가 발견됨 - + Vulkan initialization failed during boot. 부팅 중 Vulkan 초기화에 실패했습니다. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping 게임 실행 중 - + Loading Web Applet... 웹 애플릿을 불러오는 중... - - + + Disable Web Applet 웹 애플릿 비활성화 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 웹 애플릿을 비활성화하면 예기치 않은 동작이 발생할 수 있으며 슈퍼 마리오 3D 컬렉션에서만 사용해야 합니다. 웹 애플릿을 비활성화하겠습니까? (디버그 설정에서 다시 활성화할 수 있습니다.) - + The amount of shaders currently being built 현재 구축 중인 셰이더 수 - + The current selected resolution scaling multiplier. 현재 선택된 해상도 조정 배율입니다. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 현재 에뮬레이션 속도입니다. 100%보다 높거나 낮은 값은 에뮬레이션이 스위치보다 빠르거나 느리게 실행되고 있음을 나타냅니다. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 현재 게임이 표시하는 초당 프레임 수입니다. 이 값은 게임 및 장면에 따라 다릅니다. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 프레임 제한이나 수직 동기화를 제외하고 스위치 프레임을 에뮬레이션하는 데 걸리는 시간입니다. 최고 속도 에뮬레이션의 경우 최대 16.67 밀리초입니다. - + Unmute 음소거 해제 - + Mute 음소거 - + Reset Volume 볼륨 재설정 - + + Configure Effects... + + + + &Clear Recent Files 최근 파일 지우기(&C) - + &Continue 재개(&C) - + Warning: Outdated Game Format 경고: 구식 게임 형식입니다 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. 현재 이 게임에 대해 구형 형식인 분해된 ROM 디렉터리 형식을 사용하고 있습니다. 이 형식은 NCA, NAX, XCI 또는 NSP와 같은 다른 형식으로 대체되었습니다. 분해된 ROM 디렉터리에는 아이콘, 메타데이터 및 업데이트 지원 기능이 없습니다.<br>Eden에서 지원하는 다양한 스위치 형식에 대한 자세한 내용은 사용자 설명서를 참조해 주세요. 이 메시지는 다시 표시되지 않습니다. - - + + Error while loading ROM! ROM 불러오는 중 오류 발생! - + The ROM format is not supported. 해당 ROM 형식은 지원되지 않습니다. - + An error occurred initializing the video core. 비디오 코어 초기화 중 오류가 발생했습니다. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. ROM 불러오는 중 오류 발생! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>파일을 다시 덤프하거나 디스코드/Stoat에서 도움을 요청해 주세요. - + An unknown error occurred. Please see the log for more details. 알 수 없는 오류가 발생했습니다. 자세한 내용은 로그를 참조해 주세요. - - + + (64-bit) (64비트) - - + + (32-bit) (32비트) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... 소프트웨어 닫는 중... - + Save Data 저장 데이터 - + Mod Data 모드 데이터 - + Error Opening %1 Folder %1 폴더 여는 중 오류 발생 - - + + Folder does not exist! 폴더가 존재하지 않습니다! - + Remove Installed Game Contents? 설치된 게임 콘텐츠를 제거하겠습니까? - + Remove Installed Game Update? 설치된 게임 업데이트를 제거하겠습니까? - + Remove Installed Game DLC? 설치된 게임 DLC를 제거하겠습니까? - + Remove Entry 항목 제거 - + Delete OpenGL Transferable Shader Cache? OpenGL 이동 가능한 셰이더 캐시를 삭제하겠습니까? - + Delete Vulkan Transferable Shader Cache? Vulkan 이동 가능한 셰이더 캐시를 삭제하겠습니까? - + Delete All Transferable Shader Caches? 이동 가능한 셰이더 캐시를 모두 삭제하겠습니까? - + Remove Custom Game Configuration? 사용자 지정 게임 구성을 제거하겠습니까? - + Remove Cache Storage? 캐시 저장소를 제거하겠습니까? - + Remove File 파일 제거 - + Remove Play Time Data 플레이 시간 데이터 제거 - + Reset play time? 플레이 시간을 재설정하겠습니까? - - + + RomFS Extraction Failed! RomFS 추출 실패! - + There was an error copying the RomFS files or the user cancelled the operation. RomFS 파일을 복사하는 중에 오류가 발생했거나 사용자가 작업을 취소했습니다. - + Full 전체 - + Skeleton 뼈대 - + Select RomFS Dump Mode RomFS 덤프 모드 선택 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. RomFS 덤프 방식을 선택해 주세요. 전체를 선택하면 모든 파일이 새 디렉터리로 복사되고, 뼈대를 선택하면 디렉터리 구조만 생성됩니다. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 위치에 RomFS를 추출할 수 있는 여유 공간이 부족합니다. 공간을 확보하거나 에뮬레이션 > 구성 > 시스템 > 파일 시스템 > 덤프 경로에서 다른 덤프 디렉터리를 선택해 주세요. - + Extracting RomFS... RomFS 추출 중... - - + + Cancel 취소 - + RomFS Extraction Succeeded! RomFS 추출 성공! - + The operation completed successfully. 작업이 성공적으로 완료되었습니다. - + Error Opening %1 %1을(를) 여는 중 오류 발생 - + Select Directory 디렉터리 선택 - + Properties 속성 - + The game properties could not be loaded. 게임 속성을 불러올 수 없습니다. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. 스위치 실행 파일(%1);;모든 파일(*.*) - + Load File 파일 불러오기 - + Open Extracted ROM Directory 추출된 ROM 디렉터리 열기 - + Invalid Directory Selected 유효하지 않은 디렉터리 선택됨 - + The directory you have selected does not contain a 'main' file. 선택한 디렉터리에는 'main' 파일이 없습니다. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 설치 가능한 스위치 파일(*.nca, *.nsp, *.xci);;닌텐도 콘텐츠 아카이브(*.nca);;닌텐도 제출 패키지(*.nsp);;NX 카트리지 이미지(*.xci) - + Install Files 파일 설치 - + %n file(s) remaining %n개 파일 남음 - + Installing file "%1"... "%1" 파일 설치 중... + - Install Results 설치 결과 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 잠재적인 충돌을 방지하기 위해 저희는 사용자가 기본 게임을 NAND에 설치하는 것은 권장하지 않습니다. 이 기능은 업데이트 및 DLC 설치에만 사용해 주세요. - + %n file(s) were newly installed %n개 파일이 새로 설치됨 - + %n file(s) were overwritten %n개 파일이 덮어쓰기됨 - + %n file(s) failed to install %n개 파일 설치 실패 - + System Application 시스템 애플리케이션 - + System Archive 시스템 아카이브 - + System Application Update 시스템 애플리케이션 업데이트 - + Firmware Package (Type A) 펌웨어 패키지(A타입) - + Firmware Package (Type B) 펌웨어 패키지(B타입) - + Game 게임 - + Game Update 게임 업데이트 - + Game DLC 게임 DLC - + Delta Title 델타 타이틀 - + Select NCA Install Type... NCA 설치 유형 선택... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 이 NCA를 설치할 타이틀 유형을 선택해 주세요: (대부분의 경우 기본값인 '게임'을 선택하면 됩니다.) - + Failed to Install 설치 실패 - + The title type you selected for the NCA is invalid. NCA에 대해 선택한 타이틀 유형이 유효하지 않습니다. - + File not found 파일을 찾을 수 없음 - + File "%1" not found "%1" 파일을 찾을 수 없음 - + OK 확인 - + Function Disabled 기능 비활성화됨 - + Compatibility list reporting is currently disabled. Check back later! 호환성 목록 보고 옵션이 현재 비활성화되어 있습니다. 나중에 다시 확인해 주세요! - + Error opening URL URL 여는 중 오류 발생 - + Unable to open the URL "%1". "%1" URL을 열 수 없습니다. - + TAS Recording TAS 녹화 - + Overwrite file of player 1? 플레이어 1의 파일을 덮어쓰겠습니까? - + Invalid config detected 무효한 구성이 발견됨 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 휴대 모드 컨트롤러는 독 모드에서 사용할 수 없습니다. Pro 컨트롤러로 선택됩니다. - - + + Amiibo Amiibo - - + + The current amiibo has been removed 현재 amiibo가 제거됨 - + Error 오류 - - + + The current game is not looking for amiibos 현재 게임은 amiibo를 찾고 있지 않습니다 - + Amiibo File (%1);; All Files (*.*) Amiibo 파일(%1);; 모든 파일(*.*) - + Load Amiibo Amiibo 불러오기 - + Error loading Amiibo data Amiibo 데이터 불러오는 중 오류 발생 - + The selected file is not a valid amiibo 선택한 파일은 유효한 amiibo가 아닙니다 - + The selected file is already on use 선택한 파일은 이미 사용 중입니다 - + An unknown error occurred 알 수 없는 오류 발생 - + No firmware available 사용 가능한 펌웨어 없음 - + Firmware Corrupted 펌웨어 손상됨 - + Unknown applet 알 수 없는 애플릿 - + Applet doesn't map to a known value. 애플릿이 알려진 값에 매핑되지 않습니다. - + Record not found 기록을 찾을 수 없음 - + Applet not found. Please reinstall firmware. 애플릿을 찾을 수 없습니다. 펌웨어를 다시 설치해 주세요. - + Capture Screenshot 스크린샷 캡처 - + PNG Image (*.png) PNG 이미지(*.png) - + TAS state: Running %1/%2 TAS 상태: %1/%2 실행 중 - + TAS state: Recording %1 TAS 상태: %1 녹화 중 - + TAS state: Idle %1/%2 TAS 상태: %1/%2 유휴 - + TAS State: Invalid TAS 상태: 무효 - + &Stop Running 실행 중지(&S) - + Stop R&ecording 녹화 중지(&e) - + Building: %n shader(s) 구축 중: %n개 셰이더 - + Scale: %1x %1 is the resolution scaling factor 크기: %1x - + Speed: %1% / %2% 속도: %1% / %2% - + Speed: %1% 속도: %1% - + Game: %1 FPS 게임: %1 FPS - + Frame: %1 ms 프레임: %1 밀리초 - + NO AA AA 없음 - + + NO FX + + + + + FX + + + + VOLUME: MUTE 볼륨: 음소거 - + VOLUME: %1% Volume percentage (e.g. 50%) 볼륨: %1% - + Derivation Components Missing 파생 구성 요소 누락 - + Decryption keys are missing. Install them now? 복호화 키가 없습니다. 지금 설치하겠습니까? - + Wayland Detected! Wayland 발견됨! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8060,74 +8079,74 @@ Would you like to force it for future launches? 향후 출시를 위해 Wayland 사용을 강제하겠습니까? - + Use X11 X11 사용 - + Continue with Wayland Wayland와 계속 - + Don't show again 다시 보지 않기 - + Restart Required 재시작이 필요함 - + Restart Eden to apply the X11 backend. X11 백엔드를 적용하려면 Eden을 다시 시작하세요. - + Slow 저속 - + Turbo 터보 - + Unlocked 제한 해제 - + Select RomFS Dump Target RomFS 덤프 대상 선택 - + Please select which RomFS you would like to dump. 덤프할 RomFS를 선택하세요. - + Are you sure you want to close Eden? 정말 Eden을 닫겠습니까? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 정말 에뮬레이션을 중지하겠습니까? 저장되지 않은 진행 상황은 모두 손실됩니다. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/nb.ts b/dist/languages/nb.ts index 5b5d8d6ba4..191099c395 100644 --- a/dist/languages/nb.ts +++ b/dist/languages/nb.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Fullskjermmodus: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Bildeforhold: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: VSync-modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Aktiver asynkron presentasjon (kun Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Tving maksikal klokkehastighet (kun Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Kjører arbeid i bakgrunnen mens den venter på grafikkommandoer for å forhindre at GPU-en senker klokkehastigheten. - + Anisotropic Filtering: Anisotropisk filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: GPU-modus: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: DMA-nøyaktighet: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Bruk Vulkan-rørledningsbuffer - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Aktiver Reaktiv Tømming - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Synkroniser med bildefrekvensen for videoavspilling - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kjør spillet i normal hastighet under videoavspilling, selv når bildefrekvensen er låst opp. - + Barrier feedback loops Tilbakekoblingssløyfer for barrierer - + Improves rendering of transparency effects in specific games. Forbedrer gjengivelsen av transparenseffekter i spesifikke spill. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG-frø - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Enhetsnavn - + The name of the console. Konsollens navn. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Selvvalgt RTC-dato: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Språk: - + This option can be overridden when region setting is auto-select - + Region: Region: - + The region of the console. Konsollens region. - + Time Zone: Tidssone: - + The time zone of the console. Konsollens tidssone. - + Sound Output Mode: Lydutdatamodus: - + Console Mode: Konsollmodus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Bekreft før stopping av emulering - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Gjem mus under inaktivitet - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Deaktiver kontroller-appleten - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - + + + Disabled + Skrudd av + + + Never Aldri - + On Load - + Always Alltid - + CPU CPU - + GPU Skjermkort - + CPU Asynchronous - + Uncompressed (Best quality) Ukomprimert (Best kvalitet) - + BC1 (Low quality) BC1 (Lav kvalitet) - + BC3 (Medium quality) BC3 (Middels kvalitet) - + Auto Auto - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (eksperimentelt, kun AMD/Mesa) - + Null Null - + Fast Rask - - + + Accurate Nøyaktig - - + + Default Standard - + Unsafe (fast) Utrygg (raskt) - + Safe (stable) Trygg (stabil) - + Immediate - + Balanced Balansert - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) Høy (512) - - - Disabled - Skrudd av - ExtendedDynamicState 1 @@ -3201,33 +3193,38 @@ Would you like to delete the old save data? Bakgrunnsfarge: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Av - + VSync Off VSync Av - + Recommended Anbefalt - + On På - + VSync On VSync På @@ -4849,102 +4846,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6958,7 +6970,7 @@ Debug Message: - + &Pause &Pause @@ -7109,7 +7121,7 @@ Debug Message: - + &Start &Start @@ -7120,7 +7132,7 @@ Debug Message: - + R&ecord T&a opp @@ -7211,7 +7223,7 @@ Debug Message: - + None Ingen @@ -7237,722 +7249,737 @@ Debug Message: - + Small (32x32) Liten (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Stor (128x128) - + Full Size (256x256) Full størrelse (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Kjører et spill - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Slå på lyd - + Mute Demp - + Reset Volume Tilbakestill volum - + + Configure Effects... + + + + &Clear Recent Files &Tøm nylige filer - + &Continue &Fortsett - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... Lukker programvare ... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! Mappen finnes ikke! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File Fjern fil - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full Full - + Skeleton Skjelett - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... Utvinner RomFS... - - + + Cancel Avbryt - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory Velg mappe - + Properties Egenskaper - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... Installerer filen «%1» ... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application Systemapp - + System Archive Systemarkiv - + System Application Update - + Firmware Package (Type A) Fastvarepakke (Type A) - + Firmware Package (Type B) Fastvarepakke (Type B) - + Game Spill - + Game Update Spilloppdatering - + Game DLC Spill-DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found Filen ble ikke funnet - + File "%1" not found - + OK OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL Feil under åpning av URL - + Unable to open the URL "%1". - + TAS Recording TAS-opptak - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo amiibo - - + + The current amiibo has been removed - + Error Feil - - + + The current game is not looking for amiibos Det nåværende spillet ser ikke etter amiiboer - + Amiibo File (%1);; All Files (*.*) amiibo-fil (%1);; Alle filer (*.*) - + Load Amiibo Last inn amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available Ingen fastvare tilgjengelig - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot Ta skjermklipp - + PNG Image (*.png) PNG-bilde (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Hastighet: %1% / %2% - + Speed: %1% Hastighet: %1% - + Game: %1 FPS Spill: %1 FPS - + Frame: %1 ms Ramme: %1 ms - + NO AA INGEN AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE VOLUM: DEMP - + VOLUME: %1% Volume percentage (e.g. 50%) VOLUM: %1% - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7960,74 +7987,74 @@ Would you like to force it for future launches? - + Use X11 Bruk X11 - + Continue with Wayland - + Don't show again Ikke vis igjen - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow Treg - + Turbo Turbo - + Unlocked Ubegrenset - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts index 3e2831b876..8e99ce56d4 100644 --- a/dist/languages/nl.ts +++ b/dist/languages/nl.ts @@ -578,70 +578,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Volledig scherm modus: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Aspect Ratio: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC-emulatie: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: ASTC Decodeer Methode: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -650,55 +660,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: VSync-modus: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -706,426 +736,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Schakel asynchrone presentatie in (alleen Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Forceer maximale klokken (alleen Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Werkt op de achtergrond terwijl er wordt gewacht op grafische opdrachten om te voorkomen dat de GPU zijn kloksnelheid verlaagt. - + Anisotropic Filtering: Anisotrope Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Gebruik Vulkan-pijplijn-cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Schakel Reactive Flushing In - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback - + Run the game at normal speed during video playback, even when the framerate is unlocked. - + Barrier feedback loops - + Improves rendering of transparency effects in specific games. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed RNG Seed - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Apparaatnaam - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Aangepaste RTC Datum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Taal: - + This option can be overridden when region setting is auto-select - + Region: Regio: - + The region of the console. - + Time Zone: Tijdzone: - + The time zone of the console. - + Sound Output Mode: Geluidsuitvoermodus: - + Console Mode: Console Modus: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Bevestig voordat u de emulatie stopt - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Verberg muis wanneer inactief - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Controller-applet uitschakelen - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1157,155 +1153,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) - + BC1 (Low quality) BC1 (Lage Kwaliteit) - + BC3 (Medium quality) BC3 (Gemiddelde kwaliteit) - + Auto Auto - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - - + + Accurate Accuraat - - + + Default Standaard - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2082,11 +2079,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3196,33 +3188,38 @@ Would you like to delete the old save data? Achtergrondkleur: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Uit - + VSync Off VSync Uit - + Recommended Aanbevolen - + On Aan - + VSync On VSync Aan @@ -4844,102 +4841,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6954,7 +6966,7 @@ Debug Message: - + &Pause &Onderbreken @@ -7105,7 +7117,7 @@ Debug Message: - + &Start &Start @@ -7116,7 +7128,7 @@ Debug Message: - + R&ecord Opnemen @@ -7207,7 +7219,7 @@ Debug Message: - + None @@ -7233,722 +7245,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7956,74 +7983,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/pl.ts b/dist/languages/pl.ts index 14da962f1a..28a51ae4f8 100644 --- a/dist/languages/pl.ts +++ b/dist/languages/pl.ts @@ -592,12 +592,22 @@ SMAA oferuje najlepszą jakość. FXAA może zapewnić stabilniejszy obraz w niższych rozdzielczościach. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Tryb Pełnoekranowy: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,12 +616,12 @@ Tryb bez ramek zapewnia najlepszą zgodność z klawiaturą ekranową wymaganą Pełny ekran wyłączny może oferować lepszą wydajność i lepsze wsparcie FreeSync/G-Sync. - + Aspect Ratio: Format obrazu: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -620,35 +630,35 @@ Większość gier obsługuje wyłącznie 16:9, więc inne proporcje wymagają mo Kontroluje także proporcje przechwytywanych zrzutów ekranu. - + Use persistent pipeline cache Używaj trwałej pamięci podręcznej (cache) potoków - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Pozwala zapisywać shadery w pamięci masowej, aby przyspieszyć ładowanie przy kolejnych uruchomieniach gry. Wyłączanie tej opcji jest przeznaczone do debugowania. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulacja NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -657,12 +667,12 @@ Dekodowanie może być wykonywane przez CPU albo GPU, albo może być całkowici W większości przypadków najlepszą wydajność zapewnia dekodowanie na GPU. - + ASTC Decoding Method: Metoda dekodowania ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -674,12 +684,12 @@ GPU: użyj shaderów obliczeniowych GPU do dekodowania tekstur ASTC (zalecane). CPU asynchronicznie: użyj CPU do dekodowania na żądanie. Eliminuje zacięcia podczas dekodowania ASTC, ale może powodować artefakty. - + ASTC Recompression Method: Metoda rekompresji ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -687,44 +697,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: format pośredni zostanie ponownie skompresowany do BC1 lub BC3 — oszczędza VRAM kosztem jakości obrazu. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Tryb wykorzystania VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Określa, czy emulator ma oszczędzać pamięć, czy maksymalnie wykorzystywać dostępną pamięć wideo dla wydajności. Tryb agresywny może pogorszyć działanie innych aplikacji, np. oprogramowania do nagrywania. - + Skip CPU Inner Invalidation Pomiń wewnętrzne unieważnienie procesora CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Pomija niektóre unieważnienia pamięci podręcznej po stronie CPU podczas aktualizacji pamięci, zmniejszając użycie CPU i poprawiając jego wydajność. Może powodować błędy lub awarie w niektórych grach. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Tryb synchronizacji pionowej: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -735,12 +765,12 @@ Mailbox może mieć niższe opóźnienia niż FIFO i nie powoduje tearingu, ale Immediate (bez synchronizacji) wyświetla wszystko, co jest dostępne, i może powodować tearing. - + Sync Memory Operations Synchronizuj operacje pamięci - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -748,171 +778,159 @@ Unreal Engine 4 games often see the most significant changes thereof. Gry z Unreal Engine 4 mogą być najbardziej dotknięte. - + Enable asynchronous presentation (Vulkan only) Włącz asynchroniczną prezentację (tylko Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Nieznacznie poprawia wydajność, przenosząc prezentację na oddzielny wątek CPU. - + Force maximum clocks (Vulkan only) Wymuś maksymalne zegary (Tylko Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Uruchamia pracę w tle podczas oczekiwania na komendy graficzne aby GPU nie obniżało taktowania. - + Anisotropic Filtering: Filtrowanie anizotropowe: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Steruje jakością renderowania tekstur pod ostrymi kątami. Na większości GPU bezpieczną wartością jest 16x. - + GPU Mode: Tryb GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Dokładność DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Włącz asynchroniczną kompilację shaderów - + May reduce shader stutter. Może zmniejszyć zacięcia spowodowane kompilacją shaderów. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Użyj pamięci podręcznej strumienia dla Vulkana - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Włącza specyficzną dla producenta GPU pamięć podręczną potoków. Ta opcja może znacząco skrócić czas ładowania shaderów w przypadkach, gdy sterownik Vulkan nie przechowuje wewnętrznie plików pamięci podręcznej potoków. - + Enable Compute Pipelines (Intel Vulkan Only) Włącz potoki obliczeniowe (tylko Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -921,192 +939,181 @@ To ustawienie istnieje wyłącznie dla sterowników Intela i może spowodować b Na wszystkich pozostałych sterownikach potoki obliczeniowe są zawsze włączone. - + Enable Reactive Flushing Włącz reaktywne opróżnianie buforów - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Używa opróżniania reaktywnego zamiast predykcyjnego, co umożliwia dokładniejsze synchronizowanie pamięci. - + Sync to framerate of video playback Synchronizuj do liczby klatek odtwarzanego wideo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Uruchamiaj grę z normalną prędkością podczas odtwarzania wideo, nawet przy odblokowanej liczbie klatek na sekundę. - + Barrier feedback loops Pętle sprzężenia zwrotnego barier - + Improves rendering of transparency effects in specific games. Poprawia renderowanie efektów przezroczystości w niektórych grach. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State Rozszerzony stan dynamiczny - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Kontroluje liczbę funkcji, które mogą być używane w Extended Dynamic State. Wyższe poziomy pozwalają na użycie większej liczby funkcji i mogą zwiększyć wydajność, ale mogą powodować dodatkowe problemy z grafiką. - + Vertex Input Dynamic State Dynamiczny stan wejścia wierzchołków - + Enables vertex input dynamic state feature for better quality and performance. Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność. - + Sample Shading Cieniowanie próbkowe - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Pozwala wykonywać shader fragmentów dla każdej próbki we fragmencie wielokrotnie próbkowanym zamiast raz dla każdego fragmentu. Poprawia jakość grafiki kosztem wydajności. Wyższe wartości poprawiają jakość, ale obniżają wydajność. - + RNG Seed Ziarno RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Ustala ziarno generatora liczb losowych. Głównie używane do speedrunów. - + Device Name Nazwa urządzenia - + The name of the console. The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Własna data RTC - + This option allows to change the clock of the console. Can be used to manipulate time in games. Ta opcja pozwala zmienić zegar konsoli. Może służyć do manipulowania czasem w grach. - + The number of seconds from the current unix time Liczba sekund od bieżącego czasu Unix - + Language: Język: - + This option can be overridden when region setting is auto-select Ta opcja może zostać nadpisana, gdy ustawienie regionu to automatyczny wybór. - + Region: Region: - + The region of the console. Region konsoli. - + Time Zone: Strefa czasowa: - + The time zone of the console. Strefa czasowa konsoli. - + Sound Output Mode: Tryb wyjścia dźwięku: - + Console Mode: Tryb konsoli - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1115,59 +1122,47 @@ W zależności od tego ustawienia gry zmienią swoją rozdzielczość, szczegó Ustawienie na Handheld może poprawić wydajność na słabszych komputerach. - + Prompt for user profile on boot Pytaj o profil użytkownika przy uruchomieniu. - + Useful if multiple people use the same PC. Przydatne, gdy z tego samego PC korzysta wiele osób. - + Pause when not in focus Wstrzymuj, gdy okno nie jest aktywne - + Pauses emulation when focusing on other windows. Wstrzymuje emulację po przełączeniu na inne okna. - + Confirm before stopping emulation Potwierdzaj przed zatrzymaniem emulacji - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Nadpisuje monity z prośbą o potwierdzenie zatrzymania emulacji. Włączenie powoduje pominięcie takich monitów i bezpośrednie wyjście z emulacji. - + Hide mouse on inactivity Ukryj mysz przy braku aktywności - + Hides the mouse after 2.5s of inactivity. Ukrywa kursor po 2,5 s bezczynności. - - - Disable controller applet - Wyłącz aplet kontrolera - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Wymusza wyłączenie użycia apletu kontrolera w oprogramowaniu uruchamianym w emulacji. -Jeśli emulowane oprogramowanie próbuje otworzyć aplet kontrolera, jest on natychmiast zamykany. - Check for updates @@ -1199,155 +1194,156 @@ Jeśli emulowane oprogramowanie próbuje otworzyć aplet kontrolera, jest on nat Prawdziwy aplet - + + + Disabled + Wyłączone + + + Never Nigdy - + On Load Przy wczytywaniu - + Always Zawsze - + CPU CPU - + GPU GPU - + CPU Asynchronous Asynchroniczne CPU - + Uncompressed (Best quality) Brak (najlepsza jakość) - + BC1 (Low quality) BC1 (niska jakość) - + BC3 (Medium quality) BC3 (średnia jakość) - + Auto Automatyczny - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Konserwatywny - + Aggressive Agresywny - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast Szybkie - - + + Accurate Dokładny - - + + Default Domyślny - + Unsafe (fast) Niezalecane (szybkie) - + Safe (stable) Bezpieczne (stabilne) - + Immediate - + Balanced Zrównoważony - - - Strict - - Unsafe @@ -2124,11 +2120,6 @@ Jeśli emulowane oprogramowanie próbuje otworzyć aplet kontrolera, jest on nat High (512) Wysokie (512) - - - Disabled - Wyłączone - ExtendedDynamicState 1 @@ -3260,33 +3251,38 @@ Czy chcesz usunąć stare dane zapisu? Kolor tła - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Wyłączone - + VSync Off VSync wyłączony - + Recommended Zalecane - + On Włączone - + VSync On VSync aktywny @@ -4908,102 +4904,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7021,7 +7032,7 @@ Komunikat debugowania: - + &Pause &Pauza @@ -7172,7 +7183,7 @@ Komunikat debugowania: - + &Start &Start @@ -7183,7 +7194,7 @@ Komunikat debugowania: - + R&ecord &Nagraj @@ -7274,7 +7285,7 @@ Komunikat debugowania: - + None Żadna (wyłączony) @@ -7300,401 +7311,406 @@ Komunikat debugowania: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected Wykryto uszkodzoną instalację Vulkan - + Vulkan initialization failed during boot. Inicjalizacja Vulkana nie powiodła się podczas uruchamiania. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Gra uruchomiona - + Loading Web Applet... Ładowanie apletu internetowego... - - + + Disable Web Applet Wyłącz Aplet sieciowy - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Wyłączenie apletu sieciowego może prowadzić do nieprzewidywalnego działania i powinno być używane wyłącznie z Super Mario 3D All-Stars. Na pewno chcesz wyłączyć aplet sieciowy? (Tę opcję można ponownie włączyć w ustawieniach debugowania). - + The amount of shaders currently being built Liczba shaderów aktualnie budowanych - + The current selected resolution scaling multiplier. Aktualnie wybrany mnożnik skalowania rozdzielczości. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Bieżąca prędkość emulacji. Wartości wyższe lub niższe niż 100% oznaczają, że emulacja działa odpowiednio szybciej lub wolniej niż na Switchu. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Liczba klatek na sekundę aktualnie wyświetlanych przez grę. Wartość ta zależy od gry oraz od konkretnej sceny. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Czas potrzebny na emulację jednej klatki Switcha, bez uwzględniania ogranicznika klatek i synchronizacji pionowej. Dla emulacji z pełną prędkością powinno to być maksymalnie 16,67 ms. - + Unmute Wyłącz wyciszenie - + Mute Wycisz - + Reset Volume Zresetuj głośność - + + Configure Effects... + + + + &Clear Recent Files &Usuń Ostatnie pliki - + &Continue &Kontynuuj - + Warning: Outdated Game Format Ostrzeżenie: Nieaktualny format gry - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Używasz dla tej gry formatu katalogu zdekonstruowanego ROM-u, który jest przestarzały i został zastąpiony innymi formatami, takimi jak NCA, NAX, XCI czy NSP. Katalogi takich ROM-ów nie zawierają ikon, metadanych ani nie obsługują aktualizacji.<br>Aby uzyskać wyjaśnienie dotyczące różnych formatów Switcha obsługiwanych przez Eden, zajrzyj do naszego podręcznika użytkownika. Ten komunikat nie zostanie wyświetlony ponownie. - - + + Error while loading ROM! Błąd podczas wczytywania ROMu! - + The ROM format is not supported. Ten format ROMu nie jest wspierany. - + An error occurred initializing the video core. Wystąpił błąd podczas inicjowania rdzenia wideo. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Błąd podczas wczytywania ROMu! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Proszę ponownie zrzucić pliki lub poprosić o pomoc na Discordzie/Stoat. - + An unknown error occurred. Please see the log for more details. Wystąpił nieznany błąd. Więcej informacji można znaleźć w pliku log. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Zamykanie aplikacji... - + Save Data Zapis danych - + Mod Data Dane modów - + Error Opening %1 Folder Błąd podczas otwierania folderu %1 - - + + Folder does not exist! Folder nie istnieje! - + Remove Installed Game Contents? Usunąć zainstalowaną zawartość gry? - + Remove Installed Game Update? Usunąć zainstalowaną aktualizację gry? - + Remove Installed Game DLC? Usunąć zainstalowane DLC gry? - + Remove Entry Usuń wpis - + Delete OpenGL Transferable Shader Cache? Usunąć przenośną pamięć podręczną shaderów OpenGL? - + Delete Vulkan Transferable Shader Cache? Usunąć przenośną pamięć podręczną shaderów Vulkan? - + Delete All Transferable Shader Caches? Usunąć wszystkie przenośne pamięci podręczne shaderów? - + Remove Custom Game Configuration? Usunąć niestandardową konfigurację gry? - + Remove Cache Storage? Usunąć pamięć podręczną? - + Remove File Usuń plik - + Remove Play Time Data Usuń dane czasu gry - + Reset play time? Zresetować czas gry? - - + + RomFS Extraction Failed! Wyodrębnianie RomFS nie powiodło się! - + There was an error copying the RomFS files or the user cancelled the operation. Wystąpił błąd podczas kopiowania plików RomFS lub użytkownik przerwał operację. - + Full Pełny - + Skeleton Szkielet - + Select RomFS Dump Mode Wybierz tryb zrzutu RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Wybierz sposób wykonania zrzutu RomFS. <br>Tryb „Pełny” skopiuje wszystkie pliki do nowego katalogu, <br>natomiast „Szkielet” utworzy jedynie strukturę katalogów. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root Nie ma wystarczająco miejsca w %1 aby wyodrębnić RomFS. Zwolnij trochę miejsca, albo zmień ścieżkę zrzutu RomFs w Emulacja > Konfiguruj > System > System Plików > Źródło Zrzutu - + Extracting RomFS... Wypakowywanie RomFS... - - + + Cancel Anuluj - + RomFS Extraction Succeeded! Wypakowanie RomFS zakończone pomyślnie! - + The operation completed successfully. Operacja zakończona pomyślnie. - + Error Opening %1 Błąd podczas otwierania %1 - + Select Directory Wybierz folder - + Properties Właściwości - + The game properties could not be loaded. Właściwości tej gry nie mogły zostać załadowane. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Plik wykonywalny Switcha (%1);;Wszystkie pliki (*.*) - + Load File Załaduj plik - + Open Extracted ROM Directory Otwórz folder wypakowanego ROMu - + Invalid Directory Selected Wybrano niewłaściwy folder - + The directory you have selected does not contain a 'main' file. Wybrany folder nie zawiera 'głównego' pliku. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Instalacyjne pliki Switch'a (*.nca *.nsp *.xci);;Archiwum zawartości Nintendo (*.nca);;Pakiet poddany Nintendo (*.nsp);;Obraz z kartridża NX (*.xci) - + Install Files Zainstaluj pliki - + %n file(s) remaining Pozostał %n plikPozostały %n plikiPozostało %n plikówPozostało %n plików - + Installing file "%1"... Instalowanie pliku "%1"... + - Install Results Wynik instalacji - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Aby uniknąć ewentualnych konfliktów, odradzamy użytkownikom instalowanie gier na NAND. Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were newly installed %n plik został nowo zainstalowany @@ -7704,7 +7720,7 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) were overwritten %n plik został nadpisany @@ -7714,7 +7730,7 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + %n file(s) failed to install Nie udało się zainstalować %n pliku @@ -7724,314 +7740,324 @@ Proszę, używaj tej funkcji tylko do instalowania łatek i DLC. - + System Application Aplikacja systemowa - + System Archive Archiwum systemu - + System Application Update Aktualizacja aplikacji systemowej - + Firmware Package (Type A) Paczka systemowa (Typ A) - + Firmware Package (Type B) Paczka systemowa (Typ B) - + Game Gra - + Game Update Aktualizacja gry - + Game DLC Dodatek do gry - + Delta Title Tytuł Delta - + Select NCA Install Type... Wybierz typ instalacji NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Wybierz typ tytułu, do którego chcesz zainstalować ten NCA, jako: (W większości przypadków domyślna "gra" jest w porządku.) - + Failed to Install Instalacja nieudana - + The title type you selected for the NCA is invalid. Typ tytułu wybrany dla NCA jest nieprawidłowy. - + File not found Nie znaleziono pliku - + File "%1" not found Nie znaleziono pliku "%1" - + OK OK - + Function Disabled Funkcja wyłączona - + Compatibility list reporting is currently disabled. Check back later! Zgłaszanie do listy kompatybilności jest obecnie wyłączone. Spróbuj ponownie później! - + Error opening URL Błąd otwierania adresu URL - + Unable to open the URL "%1". Nie można otworzyć adresu URL "%1". - + TAS Recording Nagrywanie TAS - + Overwrite file of player 1? Nadpisać plik gracza 1? - + Invalid config detected Wykryto nieprawidłową konfigurację - + Handheld controller can't be used on docked mode. Pro controller will be selected. Nie można używać kontrolera handheld w trybie zadokowanym. Zostanie wybrany kontroler Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Amiibo zostało "zdjęte" - + Error Błąd - - + + The current game is not looking for amiibos Ta gra nie szuka amiibo - + Amiibo File (%1);; All Files (*.*) Plik Amiibo (%1);;Wszyskie pliki (*.*) - + Load Amiibo Zamontuj Amiibo - + Error loading Amiibo data Błąd podczas ładowania danych Amiibo - + The selected file is not a valid amiibo Wybrany plik nie jest poprawnym amiibo - + The selected file is already on use Wybrany plik jest już w użyciu - + An unknown error occurred Wystąpił nieznany błąd - + No firmware available Brak dostępnego firmware'u - + Firmware Corrupted Uszkodzony Firmware - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot Zrób zrzut ekranu - + PNG Image (*.png) Obrazek PNG (*.png) - + TAS state: Running %1/%2 Status TAS: Działa %1%2 - + TAS state: Recording %1 Status TAS: Nagrywa %1 - + TAS state: Idle %1/%2 Status TAS: Bezczynny %1%2 - + TAS State: Invalid Status TAS: Niepoprawny - + &Stop Running &Wyłącz - + Stop R&ecording Przestań &Nagrywać - + Building: %n shader(s) Budowanie: %n shaderBudowanie: %n shaderyBudowanie: %n shaderówBudowanie: %n shaderów - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% Prędkość: %1% / %2% - + Speed: %1% Prędkość: %1% - + Game: %1 FPS Gra: %1 FPS - + Frame: %1 ms Klatka: %1 ms - + NO AA BEZ AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE Głośność: Wyciszony - + VOLUME: %1% Volume percentage (e.g. 50%) Głośność: %1% - + Derivation Components Missing Brak komponentów wyprowadzania - + Decryption keys are missing. Install them now? - + Wayland Detected! Wykryto Waylanda! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8041,74 +8067,74 @@ Zaleca się zamiast tego używać X11. Czy chcesz wymusić jego użycie przy przyszłych uruchomieniach? - + Use X11 Używaj X11 - + Continue with Wayland Kontynuuj z Waylandem - + Don't show again Nie pokazuj ponownie - + Restart Required Wymagane ponowne uruchomienie - + Restart Eden to apply the X11 backend. Uruchom ponownie Edena, aby zastosować backend X11. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target Wybierz cel zrzutu RomFS - + Please select which RomFS you would like to dump. Proszę wybrać RomFS, jakie chcesz zrzucić. - + Are you sure you want to close Eden? Czy na pewno chcesz zamknąć Edena? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Czy na pewno chcesz zatrzymać emulację? Wszystkie niezapisane postępy zostaną utracone. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts index 36b5181730..063a1bff4f 100644 --- a/dist/languages/pt_BR.ts +++ b/dist/languages/pt_BR.ts @@ -590,12 +590,22 @@ SMAA oferece a melhor qualidade. FXAA pode produzir uma imagem mais estável em resoluções mais baixas. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Tela Cheia - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -604,12 +614,12 @@ Sem borda oferece a melhor compatibilidade com o teclado na tela que alguns jogo Tela cheia exclusiva pode oferecer melhor desempenho e melhor suporte a Freesync/Gsync. - + Aspect Ratio: Proporção do Ecrã: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -618,36 +628,36 @@ A maioria dos jogos suporta apenas 16:9, sendo necessárias modificações para Também controla a proporção das capturas de tela. - + Use persistent pipeline cache Usar cache persistente de pipeline - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Permite guardar os shaders para carregar os jogos nas execuções seguintes. Desabiltar essa opção só serve para propósitos de depuração. - + Use asynchronous GPU emulation Usar emulação assíncrona de GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Usa uma thread de CPU extra para renderização. Esta opção deve estar sempre habilitada. - + NVDEC emulation: Emulação NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -656,12 +666,12 @@ Tanto a CPU quanto a GPU podem ser utilizadas para decodificação, ou não deco Na maioria dos casos, a decodificação pela GPU fornece um melhor desempenho. - + ASTC Decoding Method: Método de Decodificação ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -674,56 +684,76 @@ CPU Assíncrona: Usa a CPU para decodificar texturas ASTC sob demanda. Elimina t mas pode apresentar artefatos visuais. - + ASTC Recompression Method: Método de Recompressão ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. A maioria das GPUs não suporta texturas ASTC e precisa descompactá-las para um formato intermediário: RGBA8.BC1/BC3: O formato intermediário será recomprimido para BC1 ou BC3, economizando VRAM mas degradando a qualidade da imagem. - + Frame Pacing Mode (Vulkan only) Modo de Ritmo de Quadros (apenas Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Controle como o emulador gerencia o ritmo de quadros para reduzir travamentos (engasgos) e deixar a taxa de quadros mais suave e consistente. - + VRAM Usage Mode: Modo de Uso da VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Define se o emulador deve priorizar a economia de memória ou fazer o uso máximo da memória de vídeo disponível para melhorar o desempenho. O modo agressivo pode impactar a performance de outros aplicativos, como softwares de gravação. - + Skip CPU Inner Invalidation Ignorar invalidação interna da CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Ignora certas invalidações de cache durante atualizações de memória, reduzindo o uso da CPU e melhorando a latência. Isso pode causar travamentos leves. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Modo de Sincronização vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -734,12 +764,12 @@ Mailbox pode ter menor latência que FIFO e não apresenta cortes, mas pode perd Immediate (sem sincronização) exibe o que estiver disponível e pode apresentar cortes. - + Sync Memory Operations Sincronizar operações de memória - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -748,126 +778,114 @@ Esta opção corrige problemas em diversos jogos, mas pode reduzir o desempenho. Jogos baseados na Unreal Engine 4 costumam apresentar as mudanças mais significativas. - + Enable asynchronous presentation (Vulkan only) Ativar apresentação assíncrona (Somente Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Melhora ligeiramente o desempenho ao mover a apresentação para uma thread de CPU separada. - + Force maximum clocks (Vulkan only) Forçar clock máximo (somente Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Executa trabalho em segundo plano aguardando pelos comandos gráficos para evitar a GPU de reduzir seu clock. - + Anisotropic Filtering: Filtro Anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Controla a qualidade da renderização de texturas em ângulos oblíquos. Seguro definir em 16x na maioria das GPUs. - + GPU Mode: Modo da GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: Precisão do DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Ativar compilação assíncrona de shaders - + May reduce shader stutter. Pode reduzir travamentos de shaders. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Acelera a decodificação de texturas 3D BCn usando processamento da GPU. Desative se ocorrerem travamentos ou falhas gráficas. - + GPU Unswizzle Max Texture Size Tamanho Máximo de Textura para GPU Unswizzle - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -876,48 +894,48 @@ A GPU é mais rápida para texturas médias e grandes, mas a CPU pode ser mais e Ajuste para encontrar o equilíbrio entre aceleração da GPU e sobrecarga da CPU. - + GPU Unswizzle Stream Size Tamanho do Fluxo de GPU Unswizzle - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Define a quantidade máxima de dados de textura (em MiB) processados por quadro. Valores mais altos podem reduzir travamentos durante o carregamento de texturas, mas podem afetar a consistência dos quadros. - + GPU Unswizzle Chunk Size Tamanho do Bloco de GPU Unswizzle - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Determina o número de fatias de profundidade processadas em uma única execução. Aumentar esse valor pode melhorar o desempenho em GPUs avançadas, mas pode causar TDR ou tempo limite do driver em hardwares mais fracos. - + Use Vulkan pipeline cache Utilizar cache de pipeline do Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Habilita o cache de pipeline da fabricante da GPU. Esta opção pode melhorar o tempo de carregamento de shaders significantemente em casos onde o driver Vulkan não armazena o cache de pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Habilitar Pipeline de Computação (Somente Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -926,194 +944,182 @@ Esta configuração existe apenas para drivers proprietários da Intel e pode ca Pipelines de computação estão sempre habilitados em outros drivers. - + Enable Reactive Flushing Habilitar Flushing Reativo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa flushing reativo ao invés de flushing preditivo, permitindo mais precisão na sincronização da memória. - + Sync to framerate of video playback Sincronizar com o framerate da reprodução de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Executa o jogo na velocidade normal durante a reprodução de vídeo, mesmo se o framerate estiver desbloqueado. - + Barrier feedback loops Ciclos de feedback de barreira - + Improves rendering of transparency effects in specific games. Melhora a renderização de efeitos de transparência em jogos específicos. - + Enable buffer history Ativar histórico de buffer - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Permite acesso a estados anteriores do buffer. Esta opção pode melhorar a qualidade de renderização e a consistência de desempenho em alguns jogos. - + Fix bloom effects Corrigir efeitos de bloom - + Removes bloom in Burnout. Remove o bloom no Burnout. - - Enable Legacy Rescale Pass - Ativar Passagem de Redimensionamento Legada - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Pode corrigir problemas de redimensionamento em alguns jogos, baseando-se no comportamento da implementação anterior. -Solução legada que corrige artefatos de linha em GPUs AMD e Intel, e cintilação de texturas cinzas em GPUs Nvidia no Luigi's Mansion 3. - - - + Extended Dynamic State Estado Dinâmico Estendido - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Controla o número de recursos que podem ser usados no Estado Dinâmico Estendido. Estados mais altos permitem mais recursos e podem aumentar o desempenho, mas também podem causar problemas gráficos adicionais. - + Vertex Input Dynamic State Estado Dinâmico de Entrada de Vértices - + Enables vertex input dynamic state feature for better quality and performance. Ativa o recurso de estado dinâmico de entrada de vértices para melhor qualidade e desempenho. - + Sample Shading Sombreamento por Amostragem - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Permite que o shader de fragmento seja processado por cada amostra em fragmentos multiamostrados, em vez de uma única vez por fragmento. Melhora a qualidade gráfica ao custo de desempenho. Valores mais altos aumentam a qualidade, mas reduzem a performance. - + RNG Seed Semente de RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Controla a semente do gerador de números aleatórios. Usado principalmente em speedruns. - + Device Name Nome do Dispositivo - + The name of the console. O nome do console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Data personalizada do RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Esta opção permite alterar o relógio do console. Pode ser usada para manipular o tempo nos jogos. - + The number of seconds from the current unix time Número de segundos a partir do tempo Unix atual - + Language: Idioma: - + This option can be overridden when region setting is auto-select Esta opção pode ser substituída quando a configuração de região estiver em seleção automática - + Region: Região: - + The region of the console. A região do console. - + Time Zone: Fuso Horário: - + The time zone of the console. O fuso horário do console. - + Sound Output Mode: Modo de saída de som - + Console Mode: Modo Console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1122,59 +1128,47 @@ Os jogos ajustarão resolução, detalhes e controladores suportados conforme es Definir como Portátil pode ajudar a melhorar o desempenho em sistemas mais fracos. - + Prompt for user profile on boot Solicitar perfil de usuário na inicialização - + Useful if multiple people use the same PC. Útil se várias pessoas usam o mesmo PC. - + Pause when not in focus Pausar quando não estiver em foco - + Pauses emulation when focusing on other windows. Pausa a emulação ao focar em outras janelas. - + Confirm before stopping emulation Confirmar antes de parar a emulação - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Substitui as confirmações ao parar a emulação. Ao ativar, ignora esses avisos e encerra diretamente a emulação. - + Hide mouse on inactivity Esconder rato quando inactivo. - + Hides the mouse after 2.5s of inactivity. Oculta o mouse após 2,5s de inatividade. - - - Disable controller applet - Desabilitar applet de controle - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Desativa forçadamente o uso do applet de controle em programas emulados. -Quando um programa tenta abrir o applet, ele é imediatamente fechado. - Check for updates @@ -1206,155 +1200,156 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.Applet real - + + + Disabled + Desativado + + + Never Nunca - + On Load Ao carregar - + Always Sempre - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Assíncrona - + Uncompressed (Best quality) Descompactado (Melhor Qualidade) - + BC1 (Low quality) BC1 (Baixa qualidade) - + BC3 (Medium quality) BC3 (Média qualidade) - + Auto Automático - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Conservador - + Aggressive Agressivo - + Vulkan Vulcano - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Shaders em Assembly, apenas NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimental, apenas AMD/Mesa) - + Null Nenhum - + Fast Rápido - - + + Accurate Preciso - - + + Default Padrão - + Unsafe (fast) Inseguro (rápido) - + Safe (stable) Seguro (estável) - + Immediate - + Balanced Balanceado - - - Strict - - Unsafe @@ -2131,11 +2126,6 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.High (512) Alto (512) - - - Disabled - Desativado - ExtendedDynamicState 1 @@ -3262,33 +3252,38 @@ Gostaria de deletar o salvamento antigo? Cor de fundo: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Desligado - + VSync Off Sincronização vertical desligada - + Recommended Recomendado - + On Ligado - + VSync On Sincronização vertical ligada @@ -4915,102 +4910,117 @@ Uma vez deletado, esses NÃO podem ser recuperados. Você tem 100% de certeza qu - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7028,7 +7038,7 @@ Mensagem de Depuração: - + &Pause &Pausa @@ -7179,7 +7189,7 @@ Mensagem de Depuração: - + &Start &Começar @@ -7190,7 +7200,7 @@ Mensagem de Depuração: - + R&ecord G&ravar @@ -7281,7 +7291,7 @@ Mensagem de Depuração: - + None Nenhum @@ -7307,723 +7317,738 @@ Mensagem de Depuração: - + Small (32x32) Pequeno (32x32) - + Standard (64x64) Padrão (64x64) - + Large (128x128) Grande (128x128) - + Full Size (256x256) Tamanho completo (256x256) - + Broken Vulkan Installation Detected Detectada Instalação Defeituosa do Vulkan - + Vulkan initialization failed during boot. Inicialização do Vulkan falhou durante o boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Rodando um jogo - + Loading Web Applet... Carregando Applet Web... - - + + Disable Web Applet Desativar Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Desativar o Applet Web pode problemas indefinidos e deverá apenas ser usando com o Super Mario 3D All-Stars. Você tem certeza que quer desativar o Applet Web? (Isso pode ser reativado nas Configurações de Depuração.) - + The amount of shaders currently being built A Quantidade total de Shaders está sendo atualmente construída - + The current selected resolution scaling multiplier. A atual multiplicação de escala de resolução selecionada. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Velocidade atual da emulação. Valores altos ou mais baixo que 100% indicam que o emulador está rodando mais rápido ou mais lento que um Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Quantos quadros por segundo o jogo está mostrando atualmente. Isto varia de jogo para jogo e de cena a cena. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory Selecionar Diretório - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory Abrir o Diretório da ROM extraída - + Invalid Directory Selected Diretório selecionado inválido - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo Amiibo - - + + The current amiibo has been removed O Amiibo atual foi removido - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8031,74 +8056,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts index beef4e100c..eb9b0c12a4 100644 --- a/dist/languages/pt_PT.ts +++ b/dist/languages/pt_PT.ts @@ -573,12 +573,22 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Tela Cheia - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -587,47 +597,47 @@ Sem borda oferece a melhor compatibilidade com o teclado na tela que alguns jogo Tela cheia exclusiva pode oferecer melhor performance e melhor suporte a Freesync/Gsync. - + Aspect Ratio: Proporção do Ecrã: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Permite guardar os shaders para carregar os jogos nas execuções seguintes. Desabiltar essa opção só serve para propósitos de depuração. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Emulação NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -636,12 +646,12 @@ Tanto a CPU quanto a GPU podem ser utilizadas para decodificação, ou não deco Na maioria dos casos, a decodificação pela GPU fornece uma melhor performance. - + ASTC Decoding Method: Método de Decodificação ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -650,55 +660,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: Método de Recompressão ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: Modo de Uso da VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Modo de Sincronização vertical: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -706,427 +736,393 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Ativar apresentação assíncrona (Somente Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Melhora ligeiramente o desempenho ao mover a apresentação para uma thread de CPU separada. - + Force maximum clocks (Vulkan only) Forçar clock máximo (somente Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Executa trabalho em segundo plano aguardando pelos comandos gráficos para evitar a GPU de reduzir seu clock. - + Anisotropic Filtering: Filtro Anisotrópico: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Utilizar cache de pipeline do Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Habilita o cache de pipeline da fabricante da GPU. Esta opção pode melhorar o tempo de carregamento de shaders significantemente em casos onde o driver Vulkan não armazena o cache de pipeline internamente. - + Enable Compute Pipelines (Intel Vulkan Only) Habilitar Pipeline de Computação (Somente Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Habilitar Flushing Reativo - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Usa flushing reativo ao invés de flushing preditivo, permitindo mais precisão na sincronização da memória. - + Sync to framerate of video playback Sincronizar com o framerate da reprodução de vídeo - + Run the game at normal speed during video playback, even when the framerate is unlocked. Executa o jogo na velocidade normal durante a reprodução de vídeo, mesmo se o framerate estiver desbloqueado. - + Barrier feedback loops Ciclos de feedback de barreira - + Improves rendering of transparency effects in specific games. Melhora a renderização de efeitos de transparência em jogos específicos. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Semente de RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Nome do Dispositivo - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Data personalizada do RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: Idioma: - + This option can be overridden when region setting is auto-select - + Region: Região: - + The region of the console. - + Time Zone: Fuso Horário: - + The time zone of the console. - + Sound Output Mode: Modo de saída de som - + Console Mode: Modo Console: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation Confirmar antes de parar a emulação - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Esconder rato quando inactivo. - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Desabilitar miniaplicativo de controle - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1158,155 +1154,156 @@ When a program attempts to open the controller applet, it is immediately closed. Miniaplicativo real - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU Assíncrona - + Uncompressed (Best quality) Descompactado (Melhor Q - + BC1 (Low quality) BC1 (Baixa qualidade) - + BC3 (Medium quality) BC3 (Média qualidade) - + Auto Automático - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Conservador - + Aggressive Agressivo - + Vulkan Vulcano - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Nenhum (desativado) - + Fast - - + + Accurate Preciso - - + + Default Padrão - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2083,11 +2080,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3201,33 +3193,38 @@ Would you like to delete the old save data? Cor de fundo: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Desligado - + VSync Off Sincronização vertical desligada - + Recommended Recomendado - + On Ligado - + VSync On Sincronização vertical ligada @@ -4849,102 +4846,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6959,7 +6971,7 @@ Debug Message: - + &Pause &Pausa @@ -7110,7 +7122,7 @@ Debug Message: - + &Start &Começar @@ -7121,7 +7133,7 @@ Debug Message: - + R&ecord G&ravar @@ -7212,7 +7224,7 @@ Debug Message: - + None @@ -7238,722 +7250,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7961,74 +7988,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts index 9488e6f43b..bb44335970 100644 --- a/dist/languages/ru_RU.ts +++ b/dist/languages/ru_RU.ts @@ -592,12 +592,22 @@ SMAA обеспечивает лучшее качество. FXAA может давать более стабильное изображение на низком разрешении. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Полноэкранный режим: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,12 +616,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp Эксклюзивный полноэкранный режим может иметь лучшую производительность и лучшую поддержку Freesync/G-Sync. - + Aspect Ratio: Соотношение сторон: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -620,35 +630,35 @@ Also controls the aspect ratio of captured screenshots. Также управляет соотношением сторон скриншотов. - + Use persistent pipeline cache Использовать постоянный кэш конвейера - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Позволяет сохранять шейдеры на диск для более быстрой загрузки при последующем запуске игры. Отключение этой функции предназначено только для отладки. - + Use asynchronous GPU emulation Использовать асинхронную эмуляцию GPU - + Uses an extra CPU thread for rendering. This option should always remain enabled. Использует дополнительный поток CPU для рендеринга. Эта опция всегда должна оставаться включенной. - + NVDEC emulation: Эмуляция NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -657,12 +667,12 @@ In most cases, GPU decoding provides the best performance. В большинстве случаев декодирование с использованием ГП обеспечивает лучшую производительность. - + ASTC Decoding Method: Метод декодирования ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -674,12 +684,12 @@ GPU: использовать вычислительные шейдеры ГП Асинхронный CPU: использовать ЦП для декодирования текстур ASTC по мере необходимости. Устраняет фризы при декодировании ASTC, но может вызывать артефакты. - + ASTC Recompression Method: Метод пересжатия ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -687,44 +697,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: промежуточный формат будет перекодирован в BC1 или BC3, экономя видеопамять, но снижая качество изображения. - + Frame Pacing Mode (Vulkan only) Режим стабилизации кадров (только Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Определяет способ управления выводом кадров для уменьшения подергиваний и повышения плавности и стабильности частоты кадров. - + VRAM Usage Mode: Режим VRAM: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Определяет, должен ли эмулятор экономить память или использовать всю доступную видеопамять для повышения производительности. Агрессивный режим может негативно сказаться на производительности других приложений, например программ записи. - + Skip CPU Inner Invalidation Пропуск внутренней инвалидизации CPU - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Пропускает некоторые операции сброса кэша при обновлении памяти, снижая нагрузку на ЦП и уменьшая задержку. Может вызывать сбои в работе игр. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Режим VSync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -735,12 +765,12 @@ Mailbox может обеспечивать меньшую задержку, ч Immediate (без синхронизации) выводит все доступные кадры и может иметь разрывы. - + Sync Memory Operations Синхронизация операций памяти - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -749,56 +779,56 @@ Unreal Engine 4 games often see the most significant changes thereof. Игры на Unreal Engine 4 чаще всего демонстрируют заметные изменения. - + Enable asynchronous presentation (Vulkan only) Включить асинхронный вывод (только Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Немного повышает производительность, перенося вывод кадра в отдельный поток CPU. - + Force maximum clocks (Vulkan only) Принудительно зафиксировать максимальную тактовую частоту (только для Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Выполняет фоновые операции во время ожидания графических команд, предотвращая снижение тактовой частоты GPU. - + Anisotropic Filtering: Анизотропная фильтрация: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Управляет качеством отображения текстур под наклонными углами. На большинстве GPU безопасно использовать значение 16x. - + GPU Mode: Режим GPU: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. Управляет режимом эмуляции ГПУ. Большинство игр нормально отображаются в режиме Быстрый, но для некоторых требуется режим Точный. Частицы обычно корректно отображаются только в режиме Точный. - + DMA Accuracy: Точность DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -807,76 +837,58 @@ Default follows the GPU Accuracy setting. По умолчанию соответствует настройке точности ГПУ. - + GPU Fence Behavior: Поведение барьеров ГПУ: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. - Управляет поведением синхронизации через барьеры ГПУ. -Мгновенный — самый быстрый вариант, но может вызывать некоторые проблемы. -Сбалансированный обеспечивает лучшую совместимость и может исправлять проблемы в некоторых играх. -Точный ещё больше повышает совместимость ценой некоторого снижения производительности. -Строгий — самый медленный вариант, но может исправлять проблемы, требующие более строгой синхронизации. -По умолчанию соответствует настройке точности ГПУ. +Default follows the GPU Mode setting. + - - Enable GPU buffer readback - Включить обратное чтение буфера ГПУ - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - Сохраняет измененные ГПУ данные путем чтения их обратно перед выгрузками. -Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. - - - + Enable asynchronous shader compilation Включить асинхронную компиляцию шейдеров - + May reduce shader stutter. Может уменьшить фризы, вызванные компиляцией шейдеров. - + GPU Clocks Частоты GPU - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. Заставляет игру думать, что GPU завершает свою работу быстрее, чем на самом деле, чтобы она перестала подстраиваться под частоты Switch, понижать разрешение и дальность прорисовки. - + GPU Unswizzle GPU-десвиззлинг (GPU Unswizzle) - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Ускоряет декодирование 3D-текстур BCn с использованием вычислений GPU. Отключите при возникновении сбоев или графических артефактов. - + GPU Unswizzle Max Texture Size Максимальный размер текстур для GPU-десвиззлинга - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -885,48 +897,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size Размер потока GPU-десвиззлинга - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Задает максимальный объем данных текстур (в МиБ), обрабатываемый за кадр. Более высокие значения могут уменьшить подергивания при загрузке текстур, но это может повлиять на стабильность кадров. - + GPU Unswizzle Chunk Size Размер блока GPU-десвиззлинга - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Определяет количество слоев глубины, обрабатываемых за один вызов. Увеличение этого значения может повысить пропускную способность на производительных GPU, но на слабом оборудовании способно вызвать TDR или тайм-ауты драйвера. - + Use Vulkan pipeline cache Использовать кэш конвейеров Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Включает кэш конвейеров, зависящий от производителя GPU. Эта опция может значительно ускорить загрузку шейдеров в тех случаях, когда драйвер Vulkan не сохраняет такие файлы кэша внутри себя. - + Enable Compute Pipelines (Intel Vulkan Only) Включить вычислительные конвейеры (только для Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -935,194 +947,182 @@ Compute pipelines are always enabled on all other drivers. На остальных драйверах вычислительные конвейеры всегда включены. - + Enable Reactive Flushing Включить реактивный сброс - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Использует реактивный сброс (Reactive Flushing) вместо прогнозирующего, обеспечивая более точную синхронизацию памяти. - + Sync to framerate of video playback Синхронизировать с частотой кадров видео - + Run the game at normal speed during video playback, even when the framerate is unlocked. Поддерживать нормальную скорость игры во время воспроизведения видео, даже если частота кадров разблокирована. - + Barrier feedback loops Циклы обратной связи барьеров - + Improves rendering of transparency effects in specific games. Улучшает отрисовку эффектов прозрачности в некоторых играх. - + Enable buffer history Включить историю буфера - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Предоставляет доступ к предыдущим состояниям буфера. В некоторых играх может повысить качество рендеринга и стабильность производительности. - + Fix bloom effects Исправить эффекты bloom - + Removes bloom in Burnout. Удаляет эффект bloom в игре Burnout. - - Enable Legacy Rescale Pass - Включить устаревший режим масштабирования - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Может исправить проблемы масштабирования в некоторых играх, используя поведение предыдущей реализации. -Это обходное решение устраняет артефакты линий на GPU AMD и Intel, а также мерцание серых текстур на GPU Nvidia в Luigis Mansion 3. - - - + Extended Dynamic State Расширенное динамическое состояние (Extended Dynamic State) - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Определяет количество функций, доступных в Extended Dynamic State. Более высокие значения позволяют использовать больше функций и иногда повышают производительность, но могут вызвать дополнительные графические проблемы. - + Vertex Input Dynamic State Динамическое состояние ввода вершин (Vertex Input Dynamic State) - + Enables vertex input dynamic state feature for better quality and performance. Включает функцию динамического состояния ввода вершин для улучшения качества и производительности. - + Sample Shading Множественное затенение (Sample Shading) - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Позволяет фрагментному шейдеру выполняться для каждого сэмпла в мультисемплированном фрагменте вместо одного раза. Повышает качество графики за счет снижения производительности. Более высокие значения повышают качество, но сильнее снижают производительность. - + RNG Seed Сид RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Задает начальное значение генератора случайных чисел. В основном используется для спидранов. - + Device Name Название устройства - + The name of the console. Имя консоли. - + Homebrew Args Аргументы Homebrew - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). Аргументы командной строки при запуске homebrew (например, -noglsl). - + Custom RTC Date: Пользовательская RTC-дата: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Позволяет изменить системные часы консоли. Может использоваться для изменения времени в играх. - + The number of seconds from the current unix time Количество секунд от текущего UNIX-времени. - + Language: Язык: - + This option can be overridden when region setting is auto-select Эта настройка может быть переопределена при автоматическом выборе региона. - + Region: Регион: - + The region of the console. Регион консоли. - + Time Zone: Часовой пояс: - + The time zone of the console. Часовой пояс консоли. - + Sound Output Mode: Режим вывода звука: - + Console Mode: Консольный режим: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1131,59 +1131,47 @@ Setting to Handheld can help improve performance for low end systems. Установка в портативный режим может повысить производительность на слабых системах. - + Prompt for user profile on boot Запрашивать профиль пользователя при запуске игры - + Useful if multiple people use the same PC. Полезно, если одним ПК пользуются несколько человек. - + Pause when not in focus Приостанавливать, если окно не активно - + Pauses emulation when focusing on other windows. Приостанавливает эмуляцию при переключении на другие окна. - + Confirm before stopping emulation Запрашивать подтверждение перед остановкой эмуляции - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Убирает запросы на подтверждение остановки эмуляции. При включении этой настройки эмуляция завершается сразу, без подтверждения. - + Hide mouse on inactivity Скрывать курсор при бездействии - + Hides the mouse after 2.5s of inactivity. Скрывает курсор мыши после 2,5 секунд бездействия. - - - Disable controller applet - Отключить апплет контроллера - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Принудительно отключает использование апплета контроллера в эмулируемых программах. -При попытке открыть апплет он сразу закрывается. - Check for updates @@ -1215,155 +1203,156 @@ When a program attempts to open the controller applet, it is immediately closed. Приложение из прошивки - + + + Disabled + Отключено + + + Never Никогда - + On Load При загрузке - + Always Всегда - + CPU CPU - + GPU GPU - + CPU Asynchronous Асинхронный CPU - + Uncompressed (Best quality) Без сжатия (наилучшее качество) - + BC1 (Low quality) BC1 (низкое качество) - + BC3 (Medium quality) BC3 (среднее качество) - + Auto Авто - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative Консервативный - + Aggressive Агрессивный - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (ассемблерные шейдеры, только для NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (экспериментальный, только для AMD/Mesa) - + Null Null - + Fast Производительность - - + + Accurate Точность - - + + Default По умолчанию - + Unsafe (fast) Небезопасно (быстро) - + Safe (stable) Безопасно (стабильно) - + Immediate Мгновенный - + Balanced Баланс - - - Strict - Строгий - Unsafe @@ -2140,11 +2129,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) Высокий (512) - - - Disabled - Отключено - ExtendedDynamicState 1 @@ -3278,33 +3262,38 @@ Would you like to delete the old save data? Цвет фона: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + + + + Off Отключена - + VSync Off VSync Off - + Recommended Рекомендуется - + On Включена - + VSync On VSync On @@ -4932,102 +4921,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7045,7 +7049,7 @@ Debug Message: - + &Pause &Пауза @@ -7196,7 +7200,7 @@ Debug Message: - + &Start &Запустить @@ -7207,7 +7211,7 @@ Debug Message: - + R&ecord З&апись @@ -7298,7 +7302,7 @@ Debug Message: - + None Никакой @@ -7324,400 +7328,405 @@ Debug Message: &В Виде Карусели - + Small (32x32) Маленький (32х32) - + Standard (64x64) Стандартный (64х64) - + Large (128x128) Большой (128х128) - + Full Size (256x256) Полноразмерный (256х256) - + Broken Vulkan Installation Detected Обнаружена поврежденная установка Vulkan - + Vulkan initialization failed during boot. Не удалось выполнить инициализацию Vulkan во время загрузки. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Запущена игра - + Loading Web Applet... Загрузка веб-апплета... - - + + Disable Web Applet Отключить веб-апплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Отключение веб-апплета может привести к неожиданному поведению и должно использоваться только, если вы хотите запустить Super Mario 3D All-Stars. Вы уверены, что хотите отключить веб-апплет? (Его можно снова включить в настройках отладки.) - + The amount of shaders currently being built Количество создаваемых шейдеров на данный момент - + The current selected resolution scaling multiplier. Текущий выбранный множитель масштабирования разрешения. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Текущая скорость эмуляции. Значения выше или ниже 100% указывают на то, что эмуляция идет быстрее или медленнее, чем на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Количество кадров в секунду в данный момент. Значение будет различаться между играми и сценами. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Время, которое нужно для эмуляции одного кадра Switch, не принимая во внимание ограничение FPS или вертикальную синхронизацию. Для эмуляции в полной скорости значение должно быть не больше 16,67 мс. - + Unmute Включить звук - + Mute Выключить звук - + Reset Volume Сбросить громкость - + + Configure Effects... + + + + &Clear Recent Files &Очистить недавние файлы - + &Continue &Продолжить - + Warning: Outdated Game Format Предупреждение: устаревший формат игры - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Для этой игры вы используете разархивированный формат ROM'а, который является устаревшим и был заменен другими, такими как NCA, NAX, XCI или NSP. В разархивированных каталогах ROM'а отсутствуют иконки, метаданные и поддержка обновлений.<br><br>Для получения информации о различных форматах Switch, поддерживаемых Eden, ознакомьтесь с руководством пользователя. Это сообщение больше не будет отображаться. - - + + Error while loading ROM! Ошибка при загрузке ROM'а! - + The ROM format is not supported. Формат ROM'а не поддерживается. - + An error occurred initializing the video core. Произошла ошибка при инициализации видеоядра. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Ошибка при загрузке ROM'а! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Пожалуйста, пересоздайте дампы ваших файлов или обратитесь за помощью в Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Произошла неизвестная ошибка. Пожалуйста, проверьте журнал для подробностей. - - + + (64-bit) (64-bit) - - + + (32-bit) (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Закрываем программу... - + Save Data Сохранения - + Mod Data Данные модов - + Error Opening %1 Folder Ошибка при открытии папки %1 - - + + Folder does not exist! Папка не существует! - + Remove Installed Game Contents? Удалить установленное содержимое игр? - + Remove Installed Game Update? Удалить установленное обновление игры? - + Remove Installed Game DLC? Удалить установленные DLC игры? - + Remove Entry Удалить запись - + Delete OpenGL Transferable Shader Cache? Удалить переносной кэш шейдеров OpenGL? - + Delete Vulkan Transferable Shader Cache? Удалить переносной кэш шейдеров Vulkan? - + Delete All Transferable Shader Caches? Удалить весь переносной кэш шейдеров? - + Remove Custom Game Configuration? Удалить пользовательскую настройку игры? - + Remove Cache Storage? Удалить кэш-хранилище? - + Remove File Удалить файл - + Remove Play Time Data Удалить данные игрового времени - + Reset play time? Сбросить время игры? - - + + RomFS Extraction Failed! Не удалось извлечь RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Произошла ошибка при копировании файлов RomFS или пользователь отменил операцию. - + Full Полный - + Skeleton Скелет - + Select RomFS Dump Mode Выберите режим дампа RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Пожалуйста, выберите, как вы хотите выполнить дамп RomFS. <br>Полный скопирует все файлы в новую папку, в то время как <br>скелет создаст только структуру папок. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root В %1 недостаточно свободного места для извлечения RomFS. Пожалуйста, освободите место или выберите другую папку для дампа в Эмуляция > Параметры > Система > Файловая система > Корень дампа - + Extracting RomFS... Извлечение RomFS... - - + + Cancel Отмена - + RomFS Extraction Succeeded! Извлечение RomFS прошло успешно! - + The operation completed successfully. Операция выполнена успешно. - + Error Opening %1 Ошибка при открытии %1 - + Select Directory Выбрать папку - + Properties Свойства - + The game properties could not be loaded. Не удалось загрузить свойства игры. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Исполняемый файл Switch (%1);;Все файлы (*.*) - + Load File Загрузить файл - + Open Extracted ROM Directory Открыть папку извлеченного ROM'а - + Invalid Directory Selected Выбрана недопустимая папка - + The directory you have selected does not contain a 'main' file. Папка, которую вы выбрали, не содержит файла 'main'. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Устанавливаемый файл Switch (*.nca *.nsp *.xci);;Архив контента Nintendo (*.nca);;Пакет подачи Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Установить файлы - + %n file(s) remaining %n файл(ов) осталось%n файл(ов) осталось%n файл(ов) осталось%n файл(ов) осталось - + Installing file "%1"... Установка "%1"... + - Install Results Результаты - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Чтобы избежать возможных конфликтов, мы не рекомендуем пользователям устанавливать игры в NAND. Пожалуйста, используйте эту функцию только для установки обновлений и DLC. - + %n file(s) were newly installed %n файл(ов) были установлены недавно @@ -7727,7 +7736,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten %n файл(ов) были перезаписаны @@ -7737,7 +7746,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install %n файл(ов) не удалось установить @@ -7747,314 +7756,324 @@ Please, only use this feature to install updates and DLC. - + System Application Системное приложение - + System Archive Системный архив - + System Application Update Обновление системного приложения - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Игра - + Game Update Обновление игры - + Game DLC DLC игры - + Delta Title Дельта-титул - + Select NCA Install Type... Выберите тип установки NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Пожалуйста, выберите тип приложения, который вы хотите установить для этого NCA: (В большинстве случаев, подходит стандартный выбор «Игра».) - + Failed to Install Ошибка установки - + The title type you selected for the NCA is invalid. Тип приложения, который вы выбрали для NCA, недействителен. - + File not found Файл не найден - + File "%1" not found Файл "%1" не найден - + OK ОК - + Function Disabled Функция отключена - + Compatibility list reporting is currently disabled. Check back later! Отправка отчетов о совместимости временно отключена. Доступ будет восстановлен позднее! - + Error opening URL Ошибка при открытии URL - + Unable to open the URL "%1". Не удалось открыть URL: "%1". - + TAS Recording Запись TAS - + Overwrite file of player 1? Перезаписать файл игрока 1? - + Invalid config detected Обнаружена недопустимая конфигурация - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативный контроллер не может быть использован в режиме док-станции. Будет выбран контроллер Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Текущий amiibo был удален - + Error Ошибка - - + + The current game is not looking for amiibos Текущая игра не ищет amiibo - + Amiibo File (%1);; All Files (*.*) Файл Amiibo (%1);; Все Файлы (*.*) - + Load Amiibo Загрузить Amiibo - + Error loading Amiibo data Ошибка загрузки данных Amiibo - + The selected file is not a valid amiibo Выбранный файл не является допустимым amiibo - + The selected file is already on use Выбранный файл уже используется - + An unknown error occurred Произошла неизвестная ошибка - + No firmware available Прошивка недоступна - + Firmware Corrupted Прошивка повреждена - + Unknown applet Неизвестный апплет - + Applet doesn't map to a known value. Апплет не соответствует ни одному известному значению. - + Record not found Запись не найдена - + Applet not found. Please reinstall firmware. Апплет не найден. Пожалуйста, переустановите прошивку. - + Capture Screenshot Сделать скриншот - + PNG Image (*.png) Изображение PNG (*.png) - + TAS state: Running %1/%2 Состояние TAS: Выполняется %1/%2 - + TAS state: Recording %1 Состояние TAS: Записывается %1 - + TAS state: Idle %1/%2 Состояние TAS: Простой %1/%2 - + TAS State: Invalid Состояние TAS: Неверное - + &Stop Running &Остановка - + Stop R&ecording &Закончить запись - + Building: %n shader(s) Компиляция %n шейдер(ов)Компиляция %n шейдер(ов)Компиляция %n шейдер(ов)Компиляция %n шейдер(ов) - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Скорость: %1% / %2% - + Speed: %1% Скорость: %1% - + Game: %1 FPS Игра: %1 FPS - + Frame: %1 ms Кадр: %1 мс - + NO AA БЕЗ СГЛАЖИВАНИЯ - + + NO FX + + + + + FX + + + + VOLUME: MUTE ГРОМКОСТЬ: ЗАГЛУШЕНА - + VOLUME: %1% Volume percentage (e.g. 50%) ГРОМКОСТЬ: %1% - + Derivation Components Missing Отсутствуют необходимые компоненты - + Decryption keys are missing. Install them now? Ключи дешифрования отсутствуют. Хотите установить их? - + Wayland Detected! Обнаружен Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8065,74 +8084,74 @@ Would you like to force it for future launches? Хотите включить X11 для последующих запусков? - + Use X11 Использовать X11 - + Continue with Wayland Продолжить с Wayland - + Don't show again Больше не показывать - + Restart Required Требуется перезапуск - + Restart Eden to apply the X11 backend. Перезапустите Eden, чтобы применить X11. - + Slow Замедлено - + Turbo Турбо - + Unlocked Разблокировано - + Select RomFS Dump Target Выбор цели для дампа RomFS - + Please select which RomFS you would like to dump. Выберите RomFS, который вы хотите извлечь. - + Are you sure you want to close Eden? Вы уверены, что хотите закрыть Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Вы уверены, что хотите остановить эмуляцию? Любой несохраненный прогресс будет потерян. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/sv.ts b/dist/languages/sv.ts index d916eb4d86..818a84cbe1 100644 --- a/dist/languages/sv.ts +++ b/dist/languages/sv.ts @@ -51,7 +51,7 @@ li.checked::marker { content: "\2612"; } Communicating with the server... - Kommunicerar med servern... + Kommunicerar med servern… @@ -192,7 +192,7 @@ Detta skulle stänga av både deras forumanvändarnamn och deras IP-adress. Moderation... - Moderering... + Moderering… @@ -375,7 +375,7 @@ Påverkar inte prestanda/stabilitet men kan göra det möjligt att läsa in HD-t 200% for a 30 FPS game is 60 FPS, and for a 60 FPS game it will be 120 FPS. Disabling it means unlocking the framerate to the maximum your PC can reach. Kontrollerar spelets maximala renderingshastighet, men det är upp till varje spel om det körs snabbare eller inte. -200% för ett spel med 30 bilder/s är 60 bilder/s, och för ett spel med 60 bilder/s blir det 120 bilder/s. +200 % för ett spel med 30 bilder/s är 60 bilder/s och för ett spel med 60 bilder/s blir det 120 bilder/s. Att inaktivera det innebär att du låser upp bildfrekvensen till det maximala som din dator kan nå. @@ -436,7 +436,7 @@ Kan bidra till att minska hackande vid lägre bildfrekvenser. Raises the clock the emulated CPU reports, which removes some FPS limiters. Weaker CPUs may see reduced performance, and certain games may behave improperly. Höjer den klockfrekvens som den emulerade processorn rapporterar, vilket tar bort vissa FPS-begränsare. -Svagare processorer kan få sämre prestanda, och vissa spel kan fungera felaktigt. +Svagare processorer kan få sämre prestanda och vissa spel kan fungera felaktigt. @@ -446,7 +446,7 @@ Svagare processorer kan få sämre prestanda, och vissa spel kan fungera felakti Set a custom value of CPU ticks. Higher values can increase performance, but may cause deadlocks. A range of 77-21000 is recommended. - Ange ett anpassat värde för CPU-ticks. Högre värden kan öka prestandan, men kan orsaka deadlocks. Ett intervall på 77-21000 rekommenderas. + Ange ett anpassat värde för CPU-ticks. Högre värden kan öka prestandan, men kan orsaka deadlocks. Ett intervall på 77 till 21000 rekommenderas. @@ -594,12 +594,22 @@ SMAA erbjuder den bästa kvaliteten. FXAA kan ge en stabilare bild i lägre upplösningar. - + + Enable post-processing effects + Aktivera efterbehandlingseffekter + + + + Applies post-processing effects to the final image. + Tillämpar efterbehandlingseffekter på den slutliga bilden. + + + Fullscreen Mode: Helskärmsläge: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -608,12 +618,12 @@ Borderless ger bäst kompatibilitet med skärmtangentbordet som vissa spel kräv Exklusiv helskärm kan ge bättre prestanda och bättre stöd för Freesync/Gsync. - + Aspect Ratio: Bildförhållande: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -622,36 +632,36 @@ De flesta spel stöder endast 16:9, så modifieringar krävs för att få andra Kontrollerar även bildförhållandet för tagna skärmdumpar. - + Use persistent pipeline cache Använd permanent pipeline-cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Gör det möjligt att spara shaders i lagringsutrymmet för snabbare laddning vid nästa spelstart. Att inaktivera detta är endast avsett för felsökning. - + Use asynchronous GPU emulation Använd asynkron GPU-emulering - + Uses an extra CPU thread for rendering. This option should always remain enabled. Använder en extra CPU-tråd för rendering. Detta alternativ bör alltid vara aktiverat. - + NVDEC emulation: NVDEC-emulering: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -660,12 +670,12 @@ Den kan antingen använda CPU eller GPU för avkodning, eller inte utföra någo I de flesta fall ger GPU-avkodning bäst prestanda. - + ASTC Decoding Method: ASTC-avkodningsmetod: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -678,12 +688,12 @@ CPU asynkront: Använd CPU:n för att avkoda ASTC-texturer vid behov. Eliminerar men kan ge artefakter. - + ASTC Recompression Method: ASTC-återkomprimeringsmetod: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -692,44 +702,64 @@ BC1/BC3: Det mellanliggande formatet kommer att komprimeras om till BC1- eller B vilket sparar VRAM men försämrar bildkvaliteten. - + Frame Pacing Mode (Vulkan only) Frame Pacing Mode (endast Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Styr hur emulatorn hanterar bildhastigheten för att minska hackighet och göra bildfrekvensen jämnare och mer konsekvent. - + VRAM Usage Mode: VRAM-användningsläge: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Väljer om emulatorn ska prioritera att spara minne eller utnyttja tillgängligt videominne maximalt för prestanda. Aggressivt läge kan påverka prestandan hos andra program, till exempel inspelningsprogram. - + Skip CPU Inner Invalidation Hoppa över CPU:ns interna ogiltigförklaring - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Hoppar över vissa cache-ogiltigförklaringar under minnesuppdateringar, vilket minskar CPU-användningen och förbättrar latensen. Detta kan orsaka mjuka krascher. - + + GPU Readback Before NCE Invalidation + GPU-återläsning före NCE-ogiltigförklaring + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + Skriver tillbaka GPU-buffertdata till gästens minne innan NCE ogiltigförklarar hela gästsidan, så att data bevaras medan CPU-lagringen fortsätter. + + + + Patch Runtime NROs for NCE + Korrigera NRO:er under körning för NCE + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + Tillämpar NCE-korrigeringar på NRO-moduler som läses in under körning. Det krävs för att vissa spel ska fungera korrekt. + + + VSync Mode: VSync-läge: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -740,61 +770,58 @@ Mailbox kan ha lägre latens än FIFO och uppvisar inte tearing, men kan tappa b Immediate (ingen synkronisering) visar allt som är tillgängligt och kan uppvisa tearing. - + Sync Memory Operations Synkronisera minnesoperationer - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. Kontrollerar datakonsistens mellan beräknings- och minnesoperationer. - Det här alternativet åtgärdar problem i spel, men kan försämra prestandan. - Unreal Engine 4-spel upplever ofta de mest betydande förändringarna av detta. - + Enable asynchronous presentation (Vulkan only) Aktivera asynkron presentation (endast Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Förbättrar prestandan något genom att flytta presentationen till en separat CPU-tråd. - + Force maximum clocks (Vulkan only) Tvinga fram maximal klockfrekvens (endast Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Körs i bakgrunden i väntan på grafikkommandon för att förhindra att GPU:n sänker sin klockhastighet. - + Anisotropic Filtering: Anisotropisk filtrering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Kontrollerar kvaliteten på texturrendering vid sneda vinklar. - -Säker att ställa in på 16x på de flesta GPU:er. +Det går att ställa in 16× på de flesta GPU:er. - + GPU Mode: GPU-läge: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -803,12 +830,12 @@ De flesta spel återges bra med Snabb, men Exakt krävs fortfarande för vissa. Partiklar återges ofta korrekt endast i läget Exakt. - + DMA Accuracy: DMA-noggrannhet: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -817,76 +844,62 @@ Osäker är snabbare, medan Säker är stabilare och kan lösa problem i vissa s Standard följer inställningen för GPU-noggrannhet. - + GPU Fence Behavior: Beteende för GPU-staket: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. Styr synkroniseringsbeteendet för GPU-staket. Omedelbar är det snabbaste alternativet, men kan orsaka vissa problem. Balanserad ger bättre kompatibilitet och kan lösa problem i vissa spel. -Exakt förbättrar kompatibiliteten ytterligare på bekostnad av viss prestanda. -Strikt är långsammast, men kan lösa problem som kräver striktare synkronisering. -Standard följer inställningen för GPU-noggrannhet. +Exakt förbättrar kompatibiliteten ytterligare, men på bekostnad av viss prestanda. +Standard följer inställningen för GPU-läge. - - Enable GPU buffer readback - Aktivera återläsning av GPU-buffert - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - Bevarar data som ändrats av GPU:n genom att läsa tillbaka dem före uppladdning. -Vissa spel kräver detta för att vissa effekter ska återges korrekt. - - - + Enable asynchronous shader compilation Aktivera asynkron shaderkompilering - + May reduce shader stutter. Kan minska shader-hackighet. - + GPU Clocks GPU-klockfrekvenser - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. Får spelet att tro att GPU-arbetet avslutas snabbare än det gör, så att det slutar sänka upplösning och ritaravstånd för att passa Switch-klockfrekvenserna. - + GPU Unswizzle GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Accelererar avkodning av BCn 3D-texturer med hjälp av GPU-beräkningar. Inaktivera om du upplever krascher eller grafiska fel. - + GPU Unswizzle Max Texture Size Maximal texturstorlek för GPU Unswizzle - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -895,48 +908,48 @@ GPU är snabbare för medelstora och stora texturer, men CPU kan vara effektivar Justera detta för att hitta balansen mellan GPU-acceleration och CPU-överbelastning. - + GPU Unswizzle Stream Size Strömstorlek för GPU Unswizzle - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Ställer in den maximala mängden texturdata (i MiB) som bearbetas per bildruta. Högre värden kan minska hackighet under texturinläsning men kan påverka bildrutans konsistens. - + GPU Unswizzle Chunk Size Chunk-storlek för GPU Unswizzle - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Bestämmer antalet djupskivor som bearbetas i en enda sändning. Att öka detta kan förbättra genomströmningen på avancerade GPU:er, men kan orsaka TDR eller drivrutinstidsgränser på svagare hårdvara. - + Use Vulkan pipeline cache Använda Vulkan pipeline-cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Aktiverar GPU-leverantörsspecifik pipeline-cache. Det här alternativet kan förbättra laddningstiden för shaders avsevärt i fall där Vulkan-drivrutinen inte lagrar pipeline-cache-filer internt. - + Enable Compute Pipelines (Intel Vulkan Only) Aktivera compute pipelines (endast Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -945,194 +958,182 @@ Denna inställning finns endast för Intels egna drivrutiner och kan orsaka kras Beräkningspipelines är alltid aktiverade på alla andra drivrutiner. - + Enable Reactive Flushing Aktivera Reactive Flushing - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Använder reaktiv rensning i stället för prediktiv rensning, vilket ger mer exakt minnessynkning. - + Sync to framerate of video playback Synkronisera med bildfrekvensen för videouppspelning - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kör spelet i normal hastighet under videouppspelning, även när bildfrekvensen är upplåst. - + Barrier feedback loops Återkopplingsloopar för barriärer - + Improves rendering of transparency effects in specific games. Förbättrar renderingen av transparenseffekter i vissa spel. - + Enable buffer history Aktivera bufferthistorik - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Aktiverar åtkomst till tidigare bufferttillstånd. Det här alternativet kan förbättra renderingskvaliteten och prestandakonsistensen i vissa spel. - + Fix bloom effects Korrigera bloom-effekter - + Removes bloom in Burnout. Tar bort bloom i Burnout. - - Enable Legacy Rescale Pass - Aktivera äldre omskalningspass - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Kan åtgärda skalningsproblem i vissa spel genom att förlita sig på beteendet från den tidigare implementeringen. -Äldre beteende som åtgärdar linjeartefakter på AMD- och Intel-GPU:er och grå texturflimmer på Nvidia-GPU:er i Luigis Mansion 3. - - - + Extended Dynamic State Utökad dynamisk status - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Kontrollerar antalet funktioner som kan användas i utökat dynamiskt tillstånd. Högre tillstånd möjliggör fler funktioner och kan öka prestandan, men kan orsaka ytterligare grafiska problem. - + Vertex Input Dynamic State Dynamiskt tillstånd för vertexinmatning - + Enables vertex input dynamic state feature for better quality and performance. Aktiverar funktionen för dynamiskt tillstånd för vertexinmatning för bättre kvalitet och prestanda. - + Sample Shading Provskuggning - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Tillåter fragment-shadern att exekveras per prov i ett multisamplade fragment istället för en gång per fragment. Förbättrar grafikens kvalitet på bekostnad av prestanda. Högre värden förbättrar kvaliteten men försämrar prestandan. - + RNG Seed RNG-frö - + Controls the seed of the random number generator. Mainly used for speedrunning. Att kontrollera fröet till slumptalsgeneratorn. Används främst för speedrunning. - + Device Name Enhetsnamn - + The name of the console. Konsolens namn. - + Homebrew Args Homebrew-argument - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). Kommandoradsargument som skickas till homebrew vid start (t.ex. -noglsl). - + Custom RTC Date: Anpassat RTC-datum: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Med det här alternativet kan du ändra klockan på konsolen. Kan användas för att manipulera tiden i spel. - + The number of seconds from the current unix time Antalet sekunder från aktuell Unix-tid - + Language: Språk: - + This option can be overridden when region setting is auto-select - Det här alternativet kan åsidosättas när regioninställningen är automatiskt vald. + Det här alternativet kan åsidosättas när regioninställningen är automatiskt vald - + Region: Region: - + The region of the console. Konsolens region. - + Time Zone: Tidszon: - + The time zone of the console. Konsolens tidszon. - + Sound Output Mode: Ljudutmatningsläge: - + Console Mode: Konsolläge: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1141,59 +1142,47 @@ Spel ändrar upplösning, detaljer och stödda kontroller beroende på denna ins Inställningen Handhållen kan förbättra prestandan för enklare system. - + Prompt for user profile on boot Fråga efter användarprofil vid uppstart - + Useful if multiple people use the same PC. Användbart om flera personer använder samma dator. - + Pause when not in focus Pausa när inte i fokus - + Pauses emulation when focusing on other windows. Pausar emulering när fokus är på andra fönster. - + Confirm before stopping emulation Bekräfta innan emuleringen stoppas - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Åsidosätter frågor om att bekräfta att emuleringen ska avslutas. Om du aktiverar den hoppar du över sådana uppmaningar och avslutar emuleringen direkt. - + Hide mouse on inactivity Dölj musen vid inaktivitet - + Hides the mouse after 2.5s of inactivity. Döljer musen efter 2,5 sekunders inaktivitet. - - - Disable controller applet - Inaktivera kontroller-appleten - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Inaktiverar med tvång användningen av kontrollerappletten i emulerade program. -När ett program försöker öppna kontrollerappletten stängs den omedelbart. - Check for updates @@ -1225,155 +1214,156 @@ När ett program försöker öppna kontrollerappletten stängs den omedelbart.Verklig applet - + + + Disabled + Inaktiverad + + + Never Aldrig - + On Load Vid inläsning - + Always Alltid - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU asynkron - + Uncompressed (Best quality) Okomprimerad (bästa kvalitet) - + BC1 (Low quality) BC1 (låg kvalitet) - + BC3 (Medium quality) BC3 (medelhög kvalitet) - + Auto Auto - + 30 FPS 30 bilder/s - + 60 FPS 60 bilder/s - + 90 FPS 90 bilder/s - + 120 FPS 120 bilder/s - + Conservative Konservativ - + Aggressive Aggressiv - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (Assembly Shaders, endast NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (Experimentell, endast AMD/Mesa) - + Null Null - + Fast Snabb - - + + Accurate Exakt - - + + Default Standard - + Unsafe (fast) Osäker (snabb) - + Safe (stable) Säker (stabil) - + Immediate Omedelbar - + Balanced Balanserad - - - Strict - Strikt - Unsafe @@ -2150,11 +2140,6 @@ När ett program försöker öppna kontrollerappletten stängs den omedelbart.High (512) Hög (512) - - - Disabled - Inaktiverad - ExtendedDynamicState 1 @@ -2997,7 +2982,7 @@ När ett program försöker öppna kontrollerappletten stängs den omedelbart. ... - ... + … @@ -3072,33 +3057,33 @@ När ett program försöker öppna kontrollerappletten stängs den omedelbart. Select Emulated NAND Directory... - Välj katalog för emulerad NAND... + Välj katalog för emulerad NAND… Select Emulated SD Directory... - Välj katalog för emulerad SD... + Välj katalog för emulerad SD… Select Save Data Directory... - Välj katalog för sparat data... + Välj katalog för sparat data… Select Gamecard Path... - Välj sökväg för Gamecard... + Välj sökväg för Gamecard… Select Dump Directory... - Välj dumpkatalog... + Välj dumpkatalog… Select Mod Load Directory... - Välj katalog för modinläsningar... + Välj katalog för modinläsningar… @@ -3156,7 +3141,7 @@ Till: %2 Migrating save data... - Migrerar sparat data... + Migrerar sparat data… @@ -3247,7 +3232,7 @@ Vill du ta bort gammalt sparat data? Select External Content Directory... - Välj katalog för externt innehåll... + Välj katalog för externt innehåll… @@ -3288,33 +3273,38 @@ Vill du ta bort gammalt sparat data? Bakgrundsfärg: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + Konfigurera effekter… + + + Off Av - + VSync Off VSync Av - + Recommended Rekommenderad - + On På - + VSync On VSync På @@ -3838,7 +3828,7 @@ Vill du ta bort gammalt sparat data? Aimed to disable SDL GUIDE button hack: synthetic GUIDE(HOME) event when SELECT(MINUS) + START(PLUS) pressed. May impact Win related trigger/rumble/etc stuff - Avsedd att stänga av SDL:s GUIDE-knappshack: syntetisk GUIDE(HOME)-händelse när SELECT(MINUS) + START(PLUS) trycks ned. Kan påverka Windows-relaterade avtryckare, vibrationer med mera. + Avsedd att stänga av SDL:s GUIDE-knappshack: syntetisk GUIDE(HOME)-händelse när SELECT(MINUS) + START(PLUS) trycks ned. Kan påverka Windows-relaterade avtryckare, vibrationer med mera @@ -4037,7 +4027,7 @@ Vill du ta bort gammalt sparat data? Modifier Range: 0% - Modifieringsintervall: 0% + Modifieringsintervall: 0 % @@ -4221,7 +4211,7 @@ Vill du ta bort gammalt sparat data? Choose a value between 0% and 100% - Välj ett värde mellan 0% och 100% + Välj ett värde mellan 0 % och 100 % @@ -4259,13 +4249,13 @@ Om du vill invertera axlarna för du joysticken först vertikalt och sedan horis Deadzone: %1% - Dödzon: %1% + Dödzon: %1 % Modifier Range: %1% - Modifieringsintervall: %1% + Modifieringsintervall: %1 % @@ -4654,8 +4644,8 @@ Om du vill invertera axlarna för du joysticken först vertikalt och sedan horis Mouse panning works better with a deadzone of 0% and a range of 100%. Current values are %1% and %2% respectively. - Muspanorering fungerar bättre med en dödzon på 0% a och ett intervall på 100%. -Nuvarande värden är %1% respektive %2%. + Muspanorering fungerar bättre med en dödzon på 0 % och ett intervall på 100 %. +Nuvarande värden är %1 % respektive %2 %. @@ -4904,7 +4894,7 @@ För att ta bort NAND-installerade uppdateringar, högerklicka på spelet i spel Once deleted, these can NOT be recovered. Are you 100% sure you want to delete them? -När de har tagits bort kan de INTE återställas. Är du 100% säker på att du vill ta bort dem? +När de har tagits bort kan de INTE återställas. Är du 100 % säker på att du vill ta bort dem? @@ -4941,103 +4931,118 @@ När de har tagits bort kan de INTE återställas. Är du 100% säker på att du - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. - ReShade FX-effekter läses in från mappen post_shaders i Eden-datakatalogen. Ändringar tillämpas direkt medan ett spel körs. + Changes apply immediately while a game is running. + Ändringarna tillämpas omedelbart medan ett spel körs. - + Enable post-processing Aktivera efterbearbetning - + Preset: Förinställning: - + Apply Tillämpa - + Reset Values Återställ värden - + Return every effect to the values the preset ships with. Återställ varje effekt till de värden som förinställningen levereras med. - + Save As... Spara som… - + Save Preset Spara förinställning - + Preset name: Förinställningsnamn: - - Delete - Ta bort + + Clear + Rensa - + + Remove every effect in use and deselect the preset. + Ta bort alla använda effekter och avmarkera förinställningen. + + + Add Effect Lägg till effekt - + + None + Ingen + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Inga effekter används. Välj en förinställning och tryck på Verkställ, eller lägg till effekter en i taget. + + + Custom chain. Pick a preset above and press Apply to replace it. Anpassad kedja. Välj en förinställning ovan och tryck på Tillämpa för att ersätta den. - + Active preset: %1 (modified). Press Reset Values to go back to how it ships. Aktiv förinställning: %1 (ändrad). Tryck på Återställ värden för att återgå till ursprungsvärdena. - + Active preset: %1 Aktiv förinställning: %1 - + Enabled Aktiverad - + Disabled Inaktiverad - + Reset to defaults Återställ standardvärden - + Effect '%1' was not found. Effekten ”%1” hittades inte. - + Effect failed to compile: %1 Effekten kunde inte kompileras: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. Inga användbara ReShade FX-effekter hittades. Placera .fx-filer i mappen post_shaders. @@ -5253,7 +5258,7 @@ UUID: %2 Deadzone: %1% - Dödzon: %1% + Dödzon: %1 % @@ -5380,7 +5385,7 @@ UUID: %2 ... - ... + … @@ -5393,7 +5398,7 @@ UUID: %2 Select TAS Load Directory... - Välj katalog för TAS-inläsningar... + Välj katalog för TAS-inläsningar… @@ -5660,7 +5665,7 @@ Dra punkterna för att ändra position, eller dubbelklicka på tabellcellerna f ... - ... + … @@ -5675,7 +5680,7 @@ Dra punkterna för att ändra position, eller dubbelklicka på tabellcellerna f Select Screenshots Path... - Välj sökväg för skärmdumpar... + Välj sökväg för skärmdumpar… @@ -5927,7 +5932,7 @@ Dra punkterna för att ändra position, eller dubbelklicka på tabellcellerna f Calculating... - Beräknar... + Beräknar… @@ -6809,7 +6814,7 @@ Felsökningsmeddelande: Loading... - Läser in... + Läser in… @@ -6819,7 +6824,7 @@ Felsökningsmeddelande: Launching... - Startar... + Startar… @@ -7036,17 +7041,17 @@ Felsökningsmeddelande: &Install Files to NAND... - &Installera filer till NAND... + &Installera filer till NAND… L&oad File... - Läs &in fil... + Läs &in fil… Load &Folder... - Läs in &mapp... + Läs in &mapp… @@ -7055,7 +7060,7 @@ Felsökningsmeddelande: - + &Pause &Paus @@ -7082,7 +7087,7 @@ Felsökningsmeddelande: Con&figure... - Kon&figurera... + Kon&figurera… @@ -7147,7 +7152,7 @@ Felsökningsmeddelande: Load/Remove &Amiibo... - Läs in/ta bort &Amiibo... + Läs in/ta bort &Amiibo… @@ -7197,16 +7202,16 @@ Felsökningsmeddelande: &Configure TAS... - &Konfigurera TAS... + &Konfigurera TAS… Configure C&urrent Game... - Konfigurera a&ktuellt spel... + Konfigurera a&ktuellt spel… - + &Start &Starta @@ -7217,7 +7222,7 @@ Felsökningsmeddelande: - + R&ecord Spela &in @@ -7308,7 +7313,7 @@ Felsökningsmeddelande: - + None Ingen @@ -7334,400 +7339,405 @@ Felsökningsmeddelande: &Karusellvy - + Small (32x32) Liten (32x32) - + Standard (64x64) Standard (64x64) - + Large (128x128) Stor (128x128) - + Full Size (256x256) Full storlek (256x256) - + Broken Vulkan Installation Detected Felaktig Vulkan-installation upptäcktes - + Vulkan initialization failed during boot. Vulkan-initialiseringen misslyckades under uppstarten. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Kör ett spel - + Loading Web Applet... - Läser in webbapplet... + Läser in webbapplet… - - + + Disable Web Applet Inaktivera webbapplet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Att inaktivera webbappletten kan leda till odefinierat beteende och bör endast användas med Super Mario 3D All-Stars. Är du säker på att du vill inaktivera webbappletten? (Detta kan återaktiveras i felsökningsinställningarna.) - + The amount of shaders currently being built Antalet shaders som för närvarande byggs - + The current selected resolution scaling multiplier. Den aktuella valda multiplikatorn för upplösningsskalning. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - Aktuell emuleringshastighet. Värden högre eller lägre än 100% indikerar att emuleringen körs snabbare eller långsammare än en Switch. + Aktuell emuleringshastighet. Värden högre eller lägre än 100 % indikerar att emuleringen körs snabbare eller långsammare än en Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Hur många bildrutor per sekund spelet för närvarande visar. Detta varierar från spel till spel och från scen till scen. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Tid som krävs för att emulera en Switch-bildruta, exklusive bildbegränsning eller v-synkronisering. För emulering i full hastighet bör detta vara högst 16,67 ms. - + Unmute Aktivera ljud - + Mute Tyst - + Reset Volume Återställ volym - + + Configure Effects... + Konfigurera effekter… + + + &Clear Recent Files &Töm tidigare filer - + &Continue &Fortsätt - + Warning: Outdated Game Format Varning: Föråldrat spelformat - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Du använder det dekonstruerade ROM-katalogformatet för detta spel, vilket är ett föråldrat format som har ersatts av andra format såsom NCA, NAX, XCI eller NSP. Dekonstruerade ROM-kataloger saknar ikoner, metadata och uppdateringsstöd.<br> För en förklaring av de olika Switch-format som Eden har stöd för, se vår användarhandbok. Detta meddelande kommer inte att visas igen. - - + + Error while loading ROM! Fel vid inläsning av ROM! - + The ROM format is not supported. ROM-formatet stöds inte. - + An error occurred initializing the video core. Ett fel uppstod vid initialiseringen av videokärnan. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. Detta orsakas vanligtvis av inaktuella grafikdrivrutiner. Se loggen för mer information. Se: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>Så här kommer du åt loggfiler</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Fel vid inläsning av ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Dumpa dina filer igen eller fråga på Discord/Stoat för hjälp. - + An unknown error occurred. Please see the log for more details. Ett okänt fel har uppstått. Se loggen för mer information. - - + + (64-bit) (64-bitar) - - + + (32-bit) (32-bitar) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... - Stänger programvara... + Stänger programvara… - + Save Data Sparat data - + Mod Data Mod-data - + Error Opening %1 Folder Fel vid öppning av mappen %1 - - + + Folder does not exist! Mappen finns inte! - + Remove Installed Game Contents? Ta bort installerat spelinnehåll? - + Remove Installed Game Update? Ta bort installerad speluppdatering? - + Remove Installed Game DLC? Ta bort installerat spel-DLC? - + Remove Entry Ta bort post - + Delete OpenGL Transferable Shader Cache? Ta bort OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? Ta bort Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? Ta bort alla Transferable Shader Caches? - + Remove Custom Game Configuration? Ta bort anpassad spelkonfiguration? - + Remove Cache Storage? Ta bort cachelagring? - + Remove File Ta bort fil - + Remove Play Time Data Ta bort data om speltid - + Reset play time? Nollställ speltid? - - + + RomFS Extraction Failed! Extrahering av RomFS misslyckades! - + There was an error copying the RomFS files or the user cancelled the operation. Det uppstod ett fel vid kopieringen av RomFS-filerna eller så avbröt användaren åtgärden. - + Full Fullständigt - + Skeleton Skelett - + Select RomFS Dump Mode Välj dumpläge för RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Välj hur du vill att RomFS ska dumpas. <br>Fullständigt kopierar alla filer till den nya katalogen, medan <br>skelett endast skapar katalogstrukturen. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - Det finns inte tillräckligt med ledigt utrymme på %1 för att extrahera RomFS. Frigör utrymme eller välj en annan dumpkatalog under Emulering > Konfigurera > System > Filsystem > Dumprot. + Det finns inte tillräckligt med ledigt utrymme på %1 för att extrahera RomFS. Frigör utrymme eller välj en annan dumpkatalog under Emulering > Konfigurera > System > Filsystem > Dumprot - + Extracting RomFS... - Extraherar RomFS... + Extraherar RomFS… - - + + Cancel Avbryt - + RomFS Extraction Succeeded! Extrahering av RomFS lyckades! - + The operation completed successfully. Operationen slutfördes. - + Error Opening %1 Fel vid öppning av %1 - + Select Directory Välj katalog - + Properties Egenskaper - + The game properties could not be loaded. Spelegenskaperna kunde inte läsas in. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Körbar Switch-fil (%1);;Alla filer (*.*) - + Load File Läs in fil - + Open Extracted ROM Directory Öppna katalog för extraherad ROM - + Invalid Directory Selected Ogiltig katalog valdes - + The directory you have selected does not contain a 'main' file. Den katalog du har valt innehåller ingen ”main”-fil. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Installerbar Switch-fil (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files Installera filer - + %n file(s) remaining %n fil återstår%n filer återstår - + Installing file "%1"... - Installerar filen "%1"... + Installerar filen "%1"… + - Install Results Installationsresultat - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. För att undvika eventuella konflikter avråder vi användare från att installera basspel på NAND. Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) were newly installed %n ny fil installerades @@ -7735,7 +7745,7 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) were overwritten %n fil skrevs över @@ -7743,7 +7753,7 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + %n file(s) failed to install %n fil gick inte att installera @@ -7751,314 +7761,324 @@ Använd endast denna funktion för att installera uppdateringar och DLC. - + System Application Systemapplikation - + System Archive Systemarkiv - + System Application Update Uppdatering för systemapplikation - + Firmware Package (Type A) Firmware-paket (Type A) - + Firmware Package (Type B) Firmware-paket (Type B) - + Game Spel - + Game Update Speluppdatering - + Game DLC DLC för spel - + Delta Title Deltatitel - + Select NCA Install Type... - Välj NCA-installationstyp... + Välj NCA-installationstyp… - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Välj vilken typ av titel du vill installera denna NCA som: (I de flesta fall är standardinställningen ”Spel” tillräcklig.) - + Failed to Install Misslyckades med att installera - + The title type you selected for the NCA is invalid. Den titeltypen du valt för NCA är ogiltig. - + File not found Filen hittades inte - + File "%1" not found Filen "%1" hittades inte - + OK Ok - + Function Disabled Funktion inaktiverad - + Compatibility list reporting is currently disabled. Check back later! Rapportering till kompatibilitetslistan är för närvarande inaktiverad. Kom tillbaka senare! - + Error opening URL Fel vid öppning av URL - + Unable to open the URL "%1". Det går inte att öppna URL:en ”%1”. - + TAS Recording TAS-inspelning - + Overwrite file of player 1? Skriv över fil för spelare 1? - + Invalid config detected Ogiltig konfiguration upptäcktes - + Handheld controller can't be used on docked mode. Pro controller will be selected. Handhållen kontroller kan inte användas i dockat läge. Pro-kontrollern kommer att väljas. - - + + Amiibo Amiibo - - + + The current amiibo has been removed - Den aktuella amiibo har tagits bort. + Den aktuella amiibo har tagits bort - + Error Fel - - + + The current game is not looking for amiibos Det aktuella spelet letar inte efter amiibos - + Amiibo File (%1);; All Files (*.*) Amiibo-fil (%1);; Alla filer (*.*) - + Load Amiibo Läs in Amiibo - + Error loading Amiibo data Fel vid läsning av Amiibo-data - + The selected file is not a valid amiibo - Den valda filen är inte en giltig amiibo. + Den valda filen är inte en giltig amiibo - + The selected file is already on use Den valda filen används redan - + An unknown error occurred Ett okänt fel uppstod - + No firmware available Ingen firmware tillgänglig - + Firmware Corrupted Firmware är skadat - + Unknown applet Okänd applet - + Applet doesn't map to a known value. Appleten mappar inte till ett känt värde. - + Record not found Posten hittades inte - + Applet not found. Please reinstall firmware. Appleten hittades inte. Installera om fast programvara. - + Capture Screenshot Ta skärmbild - + PNG Image (*.png) PNG-bild (*.png) - + TAS state: Running %1/%2 TAS-tillstånd: Kör %1/%2 - + TAS state: Recording %1 TAS-tillstånd: Spelar in %1 - + TAS state: Idle %1/%2 TAS-tillstånd: Overksam %1/%2 - + TAS State: Invalid TAS-tillstånd: Ogiltig - + &Stop Running &Stoppa körning - + Stop R&ecording Stoppa i&nspelning - + Building: %n shader(s) Bygger: %n shaderBygger: %n shaders - + Scale: %1x %1 is the resolution scaling factor Skala: %1x - + Speed: %1% / %2% - Hastighet: %1% / %2% + Hastighet: %1 % / %2 % - + Speed: %1% - Hastighet: %1% + Hastighet: %1 % - + Game: %1 FPS Spel: %1 bilder/s - + Frame: %1 ms Bildruta: %1 ms - + NO AA NO AA - + + NO FX + INGA FX + + + + FX + FX + + + VOLUME: MUTE VOLYM: TYST - + VOLUME: %1% Volume percentage (e.g. 50%) - VOLYM: %1% + VOLYM: %1 % - + Derivation Components Missing Deriveringskomponenter saknas - + Decryption keys are missing. Install them now? Avkrypteringsnycklar saknas. Installera dem nu? - + Wayland Detected! Wayland upptäcktes! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8069,74 +8089,74 @@ Det rekommenderas att använda X11 istället. Vill du tvinga det för framtida starter? - + Use X11 Använd X11 - + Continue with Wayland Fortsätt med Wayland - + Don't show again Visa inte igen - + Restart Required Omstart krävs - + Restart Eden to apply the X11 backend. Starta om Eden för att tillämpa X11-backend. - + Slow Långsam - + Turbo Turbo - + Unlocked Upplåst - + Select RomFS Dump Target Välj RomFS-dumpmål - + Please select which RomFS you would like to dump. Välj vilken RomFS du vill dumpa. - + Are you sure you want to close Eden? Är du säker på att du vill stänga Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Är du säker på att du vill stoppa emuleringen? Alla osparade framsteg kommer att gå förlorade. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -8491,7 +8511,7 @@ Fortsätt ändå? UUID must be 32 hex characters (0-9, A-F) - UUID måste vara 32 hexadecimala tecken (0-9, A-F) + UUID måste vara 32 hexadecimala tecken (0 till 9, A-F) @@ -8575,7 +8595,7 @@ Fortsätt ändå? Must be 32 hex characters (0-9, a-f) Tooltip - Måste vara 32 hexadecimala tecken (0-9, a-f) + Måste vara 32 hexadecimala tecken (0 till 9, a-f) @@ -8781,7 +8801,7 @@ p, li { white-space: pre-wrap; } Migrating, this may take a while... - Migrerar, det kan ta ett tag... + Migrerar, det kan ta ett tag… @@ -9296,7 +9316,7 @@ p, li { white-space: pre-wrap; } ... - ... + … @@ -9344,7 +9364,7 @@ p, li { white-space: pre-wrap; } Installing Firmware... - Installerar firmware... + Installerar firmware… @@ -9384,7 +9404,7 @@ p, li { white-space: pre-wrap; } Verifying integrity... - Verifierar integritet... + Verifierar integritet… @@ -9442,7 +9462,7 @@ p, li { white-space: pre-wrap; } UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!<br>Eden has detected the following save directories with no attached profile:<br>%1<br><br>The following profiles are valid:<br>%2<br><br>Click "OK" to open your save folder and fix up your profiles.<br>Hint: copy the contents of the largest or last-modified folder elsewhere, delete all orphaned profiles, and move your copied contents to the good profile.<br><br>Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br> - OVÄNTADE PROBLEM KAN UPPSTÅ OM DU INTE LÄSER DETTA! <br>Eden har upptäckt följande sparningskataloger utan bifogade profiler:<br>%1<br><br>Följande profiler är giltiga:<br>%2<br><br>Klicka på ”OK” för att öppna din sparningsmapp och fixa dina profiler.<br>Tips: kopiera innehållet i den största eller senast ändrade mappen till en annan plats, ta bort alla övergivna profiler och flytta det kopierade innehållet till den giltiga profilen.<br><br>Fortfarande förvirrad? Se hjälpsidan<a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>.<br> + OVÄNTADE PROBLEM KAN UPPSTÅ OM DU INTE LÄSER DETTA!<br>Eden har upptäckt följande sparningskataloger utan bifogade profiler:<br>%1<br><br>Följande profiler är giltiga:<br>%2<br><br>Klicka på ”OK” för att öppna din sparningsmapp och rätta till dina profiler.<br>Tips: kopiera innehållet i den största eller senast ändrade mappen till en annan plats, ta bort alla övergivna profiler och flytta det kopierade innehållet till den giltiga profilen.<br><br>Fortfarande förvirrad? Se <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>hjälpsidan</a>.<br> @@ -9464,12 +9484,12 @@ p, li { white-space: pre-wrap; } Once deleted, your data will NOT come back! Only do this if you're 100% sure you want to delete this data. När dina data har raderats kan de INTE återställas! -Gör detta endast om du är 100% säker på att du vill radera dessa data. +Gör detta endast om du är 100 % säker på att du vill radera dessa data. Clearing... - Tömmer... + Tömmer… @@ -9491,7 +9511,7 @@ Gör detta endast om du är 100% säker på att du vill radera dessa data. Exporting data. This may take a while... - Exporterar data. Detta kan ta en stund... + Exporterar data. Detta kan ta en stund… @@ -9546,7 +9566,7 @@ Gör detta endast om du är 100% säker på att du vill radera dessa data. Importing data. This may take a while... - Importerar data. Detta kan ta en stund... + Importerar data. Detta kan ta en stund… @@ -10061,12 +10081,12 @@ De flesta mods är RomFS-mods, men patchar (.pchtxt) är vanligtvis ExeFS-mods.< Successfully installed firmware version %1 - Firmware-version %1 har installerats. + Firmware-version %1 har installerats Unable to locate potential firmware NCA files - Det går inte att hitta potentiella NCA-filer för firmware. + Det går inte att hitta potentiella NCA-filer för firmware @@ -10186,7 +10206,7 @@ Detta rekommenderas om du vill dela data mellan emulatorer. 1 - 8 - 1 - 8 + 1 till 8 @@ -10596,7 +10616,7 @@ p, li { white-space: pre-wrap; } By selecting "From Eden", previous save data stored in Ryujinx will be deleted, and vice versa for "From Ryujinx". Genom att länka sparade data till Ryujinx kan både Ryujinx och Eden referera till samma sparade filer för dina spel. -Om du väljer ”Från Eden” tas tidigare sparade data bort som lagrats i Ryujinx, och vice versa för ”Från Ryujinx”. +Om du väljer ”Från Eden” tas tidigare sparade data bort som lagrats i Ryujinx och vice versa för ”Från Ryujinx”. @@ -10714,7 +10734,7 @@ Om du väljer ”Från Eden” tas tidigare sparade data bort som lagrats i Ryuj Downloading... - Hämtar ner... + Hämtar ner… diff --git a/dist/languages/tr_TR.ts b/dist/languages/tr_TR.ts index 1aea55d216..b63445c3fb 100644 --- a/dist/languages/tr_TR.ts +++ b/dist/languages/tr_TR.ts @@ -585,24 +585,34 @@ FXAA can produce a more stable picture in lower resolutions. Kullanılacak kenar yumuşatma yöntemi. SMAA en iyi kaliteyi sunar. FXAA, düşük çözünürlüklerde daha kararlı bir görüntü oluşturabilir. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Tam Ekran Modu: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. Pencereyi tam ekranda işlemek için kullanılan yöntem. Sınırsız, bazı oyunların giriş için istediği ekran klavyesi ile en iyi uyumluluğu sunar. Özel tam ekran, daha iyi performans ve daha iyi Freesync/Gsync desteği sağlayabilir. - + Aspect Ratio: En-Boy Oranı: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -611,46 +621,46 @@ Also controls the aspect ratio of captured screenshots. Ayrıca, yakalanan ekran görüntülerinin en-boy oranını da kontrol eder. - + Use persistent pipeline cache Kalıcı işlem hattı önbelleğini kullan - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Gölgelendiriclerin sonraki oyun açılışlarında daha hızlı yüklenmesi için depolama alanına kaydedilmesine olanak tanır. Devre dışı bırakılması yalnızca hata ayıklama amaçlıdır. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC emülasyonu: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. Videoların nasıl çözüleceğini belirtir. Kod çözme için CPU veya GPU kullanabilir veya hiç kod çözme işlemi yapmayabilir (videolarda siyah ekran). Çoğu durumda GPU ile kod çözme en iyi performansı sağlar. - + ASTC Decoding Method: ASTC Kod Çözme Yöntemi - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -659,55 +669,75 @@ stuttering but may present artifacts. Bu seçenek ASTC dokularının nasıl çözüleceğini kontrol eder. CPU: Kod çözme için işlemciyi kullanır. GPU: ASTC dokularını çözmek için GPU'nun hesaplama gölgelendiricilerini kullanır (önerilir). CPU Asenkron: ASTC dokularını talep üzerine çözmek için işlemciyi kullanır. ASTC kod çözme kaynaklı takılmaları giderir ancak görsel bozulmalara neden olabilir. - + ASTC Recompression Method: ASTC Yeniden Sıkıştırma Yöntemi - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. Çoğu GPU, ASTC dokuları için doğrudan desteğe sahip değildir ve bir ara formata (RGBA8) açılmalıdır. BC1/BC3: Ara format BC1 veya BC3 formatında yeniden sıkıştırılarak VRAM tasarrufu sağlar ancak görüntü kalitesini düşürür. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM Kullanım Modu - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Emülatörün belleği korumayı mı yoksa performans için mevcut video belleğini maksimum düzeyde kullanmayı mı tercih edeceğini seçer. Agresif mod, kayıt yazılımları gibi diğer uygulamaların performansını etkileyebilir. - + Skip CPU Inner Invalidation CPU Geçersiz Kılma'yı Atla - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Bellek güncellemeleri sırasında belirli önbellek geçersiz kılma işlemlerini atlayarak işlemci kullanımını azaltır ve gecikmeyi iyileştirir. Bu, hafif çökmelere neden olabilir. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: VSync Modu: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -715,427 +745,393 @@ Immediate (no synchronization) presents whatever is available and can exhibit te FIFO (VSync) kare düşürmez veya yırtılma göstermez ancak ekran yenileme hızıyla sınırlıdır. FIFO Relaxed, yavaşlamadan toparlanırken yırtılmaya izin verir. Mailbox, FIFO'dan daha düşük gecikmeye sahip olabilir ve yırtılma yapmaz ancak kare düşürebilir. Immediate (senkronizasyon yok), mevcut olanı anında sunar ve yırtılmalara neden olabilir. - + Sync Memory Operations Bellek İşlemlerini Senkronize Et - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. Hesaplama ve bellek işlemleri arasında veri tutarlılığı sağlar. Bu seçenek oyunlardaki sorunları giderir ancak performansı düşürebilir. Unreal Engine 4 oyunları genellikle bundan en önemli ölçüde etkilenenlerdir. - + Enable asynchronous presentation (Vulkan only) Asenkron sunumu etkinleştir (Yalnızca Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Sunum işlemini ayrı bir işlemci iş parçacığına taşıyarak performansı biraz artırır. - + Force maximum clocks (Vulkan only) En yüksek hızı zorla (Yalnızca Vulkan için) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Grafik komutlarını beklerken GPU'nun hızının düşmesini engellemek için arka planda görev yürütür - + Anisotropic Filtering: Anisotropic Filtering: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Eğik açılardaki doku oluşturma kalitesini kontrol eder. Çoğu grafik kartında 16x olarak ayarlanması güvenlidir. - + GPU Mode: Grafik Kartı Modu - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: DMA Doğruluğu: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Asenkron gölgelendirici derlemeyi etkinleştir - + May reduce shader stutter. Gölgelendirici/shader takılmalarını azaltabilir. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Vulkan pipeline önbelleği kullan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Grafik kartı üreticisine özel işlem hattı önbelleğini etkinleştirir. Bu seçenek, Vulkan sürücüsünün işlem hattı önbellek dosyalarını dahili olarak saklamadığı durumlarda gölgelendirici yükleme süresini önemli ölçüde iyileştirebilir. - + Enable Compute Pipelines (Intel Vulkan Only) Hesaplama İşlem Hatlarını Etkinleştir (Yalnızca Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. Bazı oyunlar için gereklidir. Bu ayar yalnızca Intel'in tescilli sürücüleri için mevcuttur ve etkinleştirilirse çökmeye neden olabilir. Hesaplama işlem hatları diğer tüm sürücülerde her zaman etkindir. - + Enable Reactive Flushing Reaktif Temizlemeyi Etkinleştir - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Tahminli temizleme yerine reaktif temizleme kullanarak daha doğru bellek senkronizasyonu sağlar. - + Sync to framerate of video playback Video oynatma kare hızına senkronize et - + Run the game at normal speed during video playback, even when the framerate is unlocked. Kare hızı kilidi açık olsa bile video oynatımı sırasında oyunu normal hızda çalıştırır. - + Barrier feedback loops Bariyer geri besleme döngüleri - + Improves rendering of transparency effects in specific games. Belirli oyunlarda şeffaflık efektlerinin oluşturulmasını iyileştirir. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State Genişletilmiş Dinamik Durum - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Genişletilmiş Dinamik Durumda kullanılabilecek özelliklerin sayısını kontrol eder. Daha yüksek durumlar daha fazla özelliğe izin verir ve performansı artırabilir, ancak ek grafik sorunlarına neden olabilir. - + Vertex Input Dynamic State Vertex Dinamik Durumu - + Enables vertex input dynamic state feature for better quality and performance. Daha iyi kalite ve performans için Vertex dinamik durum özelliğini etkinleştirir. - + Sample Shading Örnek Gölgelendirme - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Parça gölgelendiricinin, her parça için bir kez yerine çoklu örneklenmiş bir parçadaki her örnek için yürütülmesine olanak tanır. Performans pahasına grafik kalitesini artırır. Daha yüksek değerler kaliteyi artırır ancak performansı düşürür. - + RNG Seed RNG çekirdeği - + Controls the seed of the random number generator. Mainly used for speedrunning. Rastgele sayı üretecinin tohumunu kontrol eder. Esas olarak hızlı bitirme denemeleri için kullanılır. - + Device Name Cihaz İsmi - + The name of the console. Konsolun adı - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: Özel RTC Tarihi - + This option allows to change the clock of the console. Can be used to manipulate time in games. Bu seçenek konsolun saatini değiştirmeye olanak tanır. Oyunlarda zamanı manipüle etmek için kullanılabilir. - + The number of seconds from the current unix time Mevcut unix zamanından itibaren saniye sayısı - + Language: Dil: - + This option can be overridden when region setting is auto-select Bölge ayarı otomatik seçim olduğunda bu seçenek geçersiz kılınabilir. - + Region: Bölge: - + The region of the console. Konsolun bölgesi - + Time Zone: Saat Dilimi: - + The time zone of the console. Konsolun saat dilimi - + Sound Output Mode: Ses Çıkış Modu: - + Console Mode: Konsol Modu: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. Konsolun Yerleşik veya El Modunda olup olmadığını seçer. Oyunlar bu ayara bağlı olarak çözünürlüklerini, detaylarını ve desteklenen kontrolcülerini değiştirecektir. El Moduna ayarlamak, düşük seviyeli sistemler için performansı artırmaya yardımcı olabilir. - + Prompt for user profile on boot Açılışta kullanıcı profili için sor - + Useful if multiple people use the same PC. Aynı bilgisayarı birden fazla kişi kullanıyorsa yararlıdır. - + Pause when not in focus Odaklı değilken duraklat - + Pauses emulation when focusing on other windows. Diğer pencerelere odaklanıldığında emülasyonu duraklatır. - + Confirm before stopping emulation Emülasyonu durdurmadan önce onayla - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Emülasyonu durdurma onayı isteklerini/istemlerini geçersiz kılar. Etkinleştirildiğinde, bu tür istekleri/istemleri atlar ve emülasyonu doğrudan/dirket olarak kapatır. - + Hide mouse on inactivity Hareketsizlik durumunda imleci gizle - + Hides the mouse after 2.5s of inactivity. 2,5 saniye hareketsizlikten sonra fareyi gizler. - - - Disable controller applet - Kontrolcü aplikasyonunu devre dışı bırak - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Emüle edilen programlarda kontrolcü aplikasyonunun kullanımını zorla devre dışı bırakır. Bir program kontrolcü aplikasyonunu açmaya çalıştığında, aplikasyon anında kapatılır. - Check for updates @@ -1167,155 +1163,156 @@ When a program attempts to open the controller applet, it is immediately closed. Gerçek aplikasyon - + + + Disabled + Devre Dışı + + + Never Asla - + On Load Yüklemede - + Always Her zaman - + CPU CPU - + GPU GPU - + CPU Asynchronous Asenkron CPU - + Uncompressed (Best quality) Sıkıştırılmamış (En iyi kalite) - + BC1 (Low quality) BC1 (Düşük kalite) - + BC3 (Medium quality) BC3 (Orta kalite) - + Auto Otomatik - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative Muhafazakar - + Aggressive Agresif - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Boş - + Fast Hızlı - - + + Accurate Doğru - - + + Default Varsayılan - + Unsafe (fast) Güvenli Değil (hızlı) - + Safe (stable) Güvenli (Stabil) - + Immediate - + Balanced Dengeli - - - Strict - - Unsafe @@ -2092,11 +2089,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) Yüksek (512) - - - Disabled - Devre Dışı - ExtendedDynamicState 1 @@ -3228,33 +3220,38 @@ Eski kayıt verilerini silmek ister misiniz? Arkaplan Rengi: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Kapalı - + VSync Off VSync Kapalı - + Recommended Önerilen - + On Açık - + VSync On Vsync Açık @@ -4876,102 +4873,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6986,7 +6998,7 @@ Debug Message: - + &Pause &Duraklat @@ -7137,7 +7149,7 @@ Debug Message: - + &Start B&aşlat @@ -7148,7 +7160,7 @@ Debug Message: - + R&ecord K&aydet @@ -7239,7 +7251,7 @@ Debug Message: - + None @@ -7265,722 +7277,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected Bozuk Vulkan Kurulumu Algılandı - + Vulkan initialization failed during boot. Açılış sırasında Vulkan başlatma işlemi başarısız oldu. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Bir oyun çalıştırılıyor - + Loading Web Applet... Web Uygulaması Yükleniyor... - - + + Disable Web Applet Web Uygulamasını Devre Dışı Bırak - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute Sesi aç - + Mute Sessize al - + Reset Volume Sesi Sıfırla - + + Configure Effects... + + + + &Clear Recent Files &Son Dosyaları Temizle - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL URL açılırken hata oluştu - + Unable to open the URL "%1". - + TAS Recording TAS İşlemi Kaydı - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Mevcut Amiibo kaldırıldı - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted Donanım Yazılımı/Firmware Bozuk - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 TAS durumu: %1/%2 Çalışıyor - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor Ölçek: %1x - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7988,74 +8015,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/uk.ts b/dist/languages/uk.ts index ecaec4fa23..fd166bc4f9 100644 --- a/dist/languages/uk.ts +++ b/dist/languages/uk.ts @@ -429,13 +429,14 @@ Can help reduce stuttering at lower framerates. CPU Clocks - + Тактові частоти ЦП Raises the clock the emulated CPU reports, which removes some FPS limiters. Weaker CPUs may see reduced performance, and certain games may behave improperly. - + Збільшує тактову частоту, про яку повідомляє емульований ЦП, завдяки чому прибираються деякі обмеження частоти кадрів. +На слабших ЦП це може спричинити зменшення продуктивності, а деякі ігри можуть неправильно відтворюватися. @@ -593,12 +594,22 @@ SMAA забезпечує найкращу якість. FXAA може створювати стабільніше зображення при низьких роздільностях. - + + Enable post-processing effects + Увімкнути ефекти постобробки + + + + Applies post-processing effects to the final image. + Застосовує ефекти постобробки до фінального зображення. + + + Fullscreen Mode: Повноекранний режим: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -607,12 +618,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp «Ексклюзивний повноекранний» може надати кращі продуктивність і підтримку Freesync/Gsync. - + Aspect Ratio: Співвідношення сторін: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -621,36 +632,36 @@ Also controls the aspect ratio of captured screenshots. Також керує співвідношеням сторін знімків екрана. - + Use persistent pipeline cache Використовувати стійкий кеш конвеєра - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. Дозволяє зберігати шейдери на накопичувачі для швидшого завантаження під час наступних запусків гри. Вимкнення цього налаштування задумане лише для зневадження. - + Use asynchronous GPU emulation Використовувати асинхронну емуляцію ГП - + Uses an extra CPU thread for rendering. This option should always remain enabled. Використовує додатковий потік ЦП для візуалізації. Це налаштування повинно завжди залишатися увімкненим. - + NVDEC emulation: Емуляція NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -659,12 +670,12 @@ In most cases, GPU decoding provides the best performance. У більшості випадків декодування за допомогою ГП забезпечує найкращу продуктивність. - + ASTC Decoding Method: Метод декодування ASTC: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -676,12 +687,12 @@ stuttering but may present artifacts. Асинхронно ЦП: Використання ЦП для декодування ASTC-текстур по мірі їх викликів. Повністю усуває затримки декодування ASTC ціною проблем з візуалізацією, поки текстури декодуються. - + ASTC Recompression Method: Метод перестиснення ASTC: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -689,44 +700,64 @@ BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, BC1/BC3: Проміжний формат буде перепаковано у формат BC1 або BC3 для збереження відеопам’яті, але це негатривно вплине на якість зображення. - + Frame Pacing Mode (Vulkan only) Режим виведення кадрів (лише Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. Керує тим, як емулятор виконує виведення кадрів, щоб зменшити затримки й забезпечити плавнішу й стабільнішу частоту кадрів. - + VRAM Usage Mode: Режим використання відеопам’яті: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. Це налаштування вибирає, чи повинен емулятор надавати перевагу заощадженню пам’яті, чи по максимуму використовувати доступну відеопам’ять задля продуктивності. Режим «Агресивно» може вплинути на продуктивність інших застосунків, як-от засоби запису. - + Skip CPU Inner Invalidation Пропускати внутрішнє анулювання ЦП - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. Пропускає деякі анулювання кешу під час оновлень пам’яті, зменшуючи використання ЦП й виправляючи затримки. Це може спричинити збої. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Режим вертикальної синхронізації: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -737,12 +768,12 @@ Mailbox може мати меншу затримку, ніж FIFO, і не ма Immediate (без синхронізації) показує всі кадри й може створювати розриви. - + Sync Memory Operations Синхронізувати операції з пам’яттю - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -751,44 +782,44 @@ Unreal Engine 4 games often see the most significant changes thereof. Ігри на Unreal Engine 4 часто зазнають найзначніших змін. - + Enable asynchronous presentation (Vulkan only) Увімкнути асинхронне подання (лише Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. Трохи покращує продуктивність завдяки переміщенню подання на окремий потік ЦП. - + Force maximum clocks (Vulkan only) Примусово максимальна тактова частота (лише Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Виконує роботу у фоновому режимі в очікуванні графічних команд, не даючи змоги ГП знижувати тактову частоту. - + Anisotropic Filtering: Анізотропна фільтрація: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. Керує якістю візуалізації текстур під непрямими кутами. Для більшості ГП можна вільно вибирати 16x. - + GPU Mode: Режим ГП: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -797,82 +828,70 @@ Particles tend to only render correctly with Accurate mode. Частинки зазвичай правильно візуалізуються лише з режимом «Точно». - + DMA Accuracy: Точність DMA: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation Увімкнути асинхронну компіляцію шейдерів - + May reduce shader stutter. Може зменшити шейдерні затримки. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle Розпакування за допомогою ГП - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. Прискорює декодування 3D-текстур BCn застосовуючи обчислення за допомогою ГП. Вимкніть у разі збоїв або проблем із графікою. - + GPU Unswizzle Max Texture Size Максимальний розмір текстур для розпакування за допомогою ГП - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -881,48 +900,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size Розмір потоку розпакування за допомогою ГП - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. Встановлює максимальний обсяг даних текстур (у МіБ) для обробки на кадр. Вищі значення здатні зменшити затримки під час завантаження текстур, але можуть вплинути на стабільність кадрів. - + GPU Unswizzle Chunk Size Розмір блоків розпакування за допомогою ГП - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. Визначає кількість зрізів глибини, оброблених за одне відправлення. Збільшення здатне покращити пропускну здатність на потужних ГП, але може призвести до TDR або затримок драйвера зі слабшим устаткуванням. - + Use Vulkan pipeline cache Використовувати кеш конвеєра Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. Вмикає особливий для різних виробників ГП кеш конвеєра. Це налаштування може значно зменшити час завантаження шейдерів у випадках, коли драйвер Vulkan не зберігає власний кеш конвеєра. - + Enable Compute Pipelines (Intel Vulkan Only) Увімкнути обчислювальні конвеєри (лише Intel Vulkan) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -931,194 +950,182 @@ Compute pipelines are always enabled on all other drivers. Обчислювальні конвеєри завжди увімкнені у всіх інших драйверах. - + Enable Reactive Flushing Увімкнути реактивне очищення - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. Використовує реактивне очищення замість прогнозованого, забезпечуючи точнішу синхронізацію пам’яті. - + Sync to framerate of video playback Синхронізувати частоту кадрів з відтворенням відео - + Run the game at normal speed during video playback, even when the framerate is unlocked. Відтворювати гру з нормальною швидкістю під час відтворення відео навіть при розблокованій частоті кадрів. - + Barrier feedback loops Бар’єрні цикли відгуку - + Improves rendering of transparency effects in specific games. Покращує візуалізацію ефектів прозорості в деяких іграх. - + Enable buffer history Увімкнути історію буфера - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Вмикає доступ до попередніх станів буфера. Цей параметр може покращити якість візуалізації та стабільну продуктивність у деяких іграх. - + Fix bloom effects Виправити ефекти світіння - + Removes bloom in Burnout. Прибирає світіння в Burnout. - - Enable Legacy Rescale Pass - Увімкнути застаріле масштабування - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - Може виправити проблеми з масштабуванням в іграх, покладаючись на поведінку з попередньої імплементації. -Застаріле масштабування виправляє артефакти з лініями на ГП від AMD та Intel, а також сіре блимання текстур на ГП від Nvidia в Luigis Mansion 3. - - - + Extended Dynamic State Розширений динамічний стан - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. Керує кількістю функцій, які можна використовувати в «Розширеному динамічному стані». Вищі значення допускають більше функцій і можуть збільшити продуктивність, але можуть спричинити додаткові проблеми з графікою. - + Vertex Input Dynamic State Динамічний стан введення вершин - + Enables vertex input dynamic state feature for better quality and performance. Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності. - + Sample Shading Шейдинг зразків - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. Дозволяє виконувати фрагмент шейдера для кожного зразка в багатозразковому фрагменті замість одного разу для кожного фрагмента. Покращує якість графікі ціною втрати продуктивності. Вищі значення покращують якість, але погіршують продуктивність. - + RNG Seed Початкове значення RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. Керує початковим значення генератора випадкових чисел. Зазвичай використовується в спідранах. - + Device Name Назва пристрою - + The name of the console. Назва консолі. - + Homebrew Args Параметри запуску Homebrew - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). Параметри командного рядка, що передаються Homebrew при запуску (як-от noglsl). - + Custom RTC Date: Користувацька дата RTC: - + This option allows to change the clock of the console. Can be used to manipulate time in games. Це налаштування дозволяє змінити час годинника консолі. Можна використовувати для маніпуляцій із часом в іграх. - + The number of seconds from the current unix time Кількість секунд від поточного unix-часу. - + Language: Мова: - + This option can be overridden when region setting is auto-select Це налаштування може перевизначитися, якщо налаштування регіону вибирається автоматично - + Region: Регіон: - + The region of the console. Регіон консолі. - + Time Zone: Часовий пояс: - + The time zone of the console. Часовий пояс консолі. - + Sound Output Mode: Режим виведення звуку: - + Console Mode: Режим консолі: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1127,59 +1134,47 @@ Setting to Handheld can help improve performance for low end systems. Налаштування «Портативний» може покращити продуктивність на слабких системах. - + Prompt for user profile on boot Запитувати профіль користувача під час запуску - + Useful if multiple people use the same PC. Корисно, якщо одним комп’ютером користуються кілька користувачів. - + Pause when not in focus Призупиняти, якщо не у фокусі - + Pauses emulation when focusing on other windows. Призупиняє емуляцію при фокусування на інших вікнах. - + Confirm before stopping emulation Підтверджувати зупинку емуляції - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. Перевизначає запити на підтвердження зупинки емуляції. Увімкнення обходить такі запити й одразу зупиняє емуляцію. - + Hide mouse on inactivity Приховувати курсор миші при бездіяльності - + Hides the mouse after 2.5s of inactivity. Приховує курсор миші після 2,5 с її бездіяльності. - - - Disable controller applet - Вимкнути аплет контролера - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - Примусово вимикає використання в емульованих програмах аплета контролера. -Якщо програма спробує відкрити аплет контролера, він одразу закриється. - Check for updates @@ -1211,155 +1206,156 @@ When a program attempts to open the controller applet, it is immediately closed. Справжній аплет - + + + Disabled + Вимкнено + + + Never Ніколи - + On Load При завантаженні - + Always Завжди - + CPU ЦП - + GPU ГП - + CPU Asynchronous Асинхронно ЦП - + Uncompressed (Best quality) Без стиснення (Найкраща якість) - + BC1 (Low quality) ВС1 (Низька якість) - + BC3 (Medium quality) ВС3 (Середня якість) - + Auto Автоматично - + 30 FPS 30 к/с - + 60 FPS 60 к/с - + 90 FPS 90 к/с - + 120 FPS 120 к/с - + Conservative Заощадження - + Aggressive Агресивно - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (асемблерні шейдери, лише NVIDIA) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (експериментально, лише AMD/Mesa) - + Null Нічого - + Fast Швидко - - + + Accurate Точно - - + + Default Стандартно - + Unsafe (fast) Небезпечно (швидко) - + Safe (stable) Безпечно (стабільно) - + Immediate - + Balanced Збалансовано - - - Strict - - Unsafe @@ -2136,11 +2132,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) Високо (512) - - - Disabled - Вимкнено - ExtendedDynamicState 1 @@ -3274,33 +3265,38 @@ Would you like to delete the old save data? Колір тла: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + + + + Off Вимкнено - + VSync Off Вертикальну синхронізацію вимкнено - + Recommended Рекомендовано - + On Увімкнено - + VSync On Вертикальну синхронізацію увімкнено @@ -4927,102 +4923,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -7040,7 +7051,7 @@ Debug Message: - + &Pause [&P] Призупинити @@ -7191,7 +7202,7 @@ Debug Message: - + &Start [&S] Запустити @@ -7202,7 +7213,7 @@ Debug Message: - + R&ecord [&E] Запис @@ -7293,7 +7304,7 @@ Debug Message: - + None Жодного @@ -7319,400 +7330,405 @@ Debug Message: - + Small (32x32) Маленький (32х32) - + Standard (64x64) Стандартний (64х64) - + Large (128x128) Великий (128х128) - + Full Size (256x256) Повнорозмірний (256х256) - + Broken Vulkan Installation Detected Виявлено пошкоджене встановлення Vulkan - + Vulkan initialization failed during boot. Не вдалося ініціалізувати Vulkan під час запуску. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping Запущено гру - + Loading Web Applet... Завантаження вебаплета... - - + + Disable Web Applet Вимкнути вебаплет - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) Вимкнення вебапплета може призвести до несподіваної поведінки, і це слід робити лише для Super Mario 3D All-Stars. Ви впевнені, що хочете вимкнути вебапплет? (Його можна знову увімкнути в налаштуваннях зневадження.) - + The amount of shaders currently being built Кількість наразі створених шейдерів - + The current selected resolution scaling multiplier. Наразі вибраний множник масштабування роздільності. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. Поточна швидкість емуляції. Значення вище або нижче 100% вказують на те, що емуляція йде швидше або повільніше, ніж на Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. Частота кадрів, яку наразі показує гра. Значення змінюватиметься залежно від гри та з кожною сценою. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. Час, потрібний для емуляції 1 кадру Switch, не враховуючи обмеження частоти кадрів або вертикальну синхронізацію. Для повношвидкісної емуляції значення повинно бути не вище 16,67 мс. - + Unmute Увімкнути звук - + Mute Вимкнути звук - + Reset Volume Скинути гучність - + + Configure Effects... + + + + &Clear Recent Files [&C] Очистити нещодавні файли - + &Continue [&C] Продовжити - + Warning: Outdated Game Format Увага: Застарілий формат гри - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. Для цієї гри ви використовуєте формат теки з деконструйованим ROM, який є застарілим форматом, заміненим на інші, як-от NCA, NAX, XCI, або NSP. У тек із деконструйованими ROM немає значків, метаданих, а також вони не підтримують оновлення.<br>Для подробиць стосовно різноманітних форматів Switch, які підтримує Eden, ознайомтеся з нашим посібником користувача. Це повідомлення не буде показано знову. - - + + Error while loading ROM! Помилка під час завантаження ROM! - + The ROM format is not supported. Непідтримуваний формат ROM. - + An error occurred initializing the video core. Сталася помилка під час ініціалізації відеоядра. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. Помилка під час завантаження ROM! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>Створіть новий дамп файлів або зверніться по допомогу в Discord/Stoat. - + An unknown error occurred. Please see the log for more details. Сталася невідома помилка. Ознайомтеся з журналом, щоб дізнатися подробиці. - - + + (64-bit) (64-бітовий) - - + + (32-bit) (32-бітовий) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... Закриття програмного засобу... - + Save Data Дані збережень - + Mod Data Дані модів - + Error Opening %1 Folder Помилка під час відкриття теки «%1» - - + + Folder does not exist! Теки не існує! - + Remove Installed Game Contents? Вилучити встановлений вміст гри? - + Remove Installed Game Update? Вилучити встановлені оновлення гри? - + Remove Installed Game DLC? Вилучити встановлені доповнення гри? - + Remove Entry Вилучити запис - + Delete OpenGL Transferable Shader Cache? Видалити переміщуваний кеш шейдерів OpenGL? - + Delete Vulkan Transferable Shader Cache? Видалити переміщуваний кеш шейдерів Vulkan? - + Delete All Transferable Shader Caches? Видалити весь переміщуваний кеш шейдерів? - + Remove Custom Game Configuration? Вилучити користувацькі налаштування гри? - + Remove Cache Storage? Вилучити сховище кешу? - + Remove File Вилучити файл - + Remove Play Time Data Вилучити дані награного часу - + Reset play time? Скинути награний час? - - + + RomFS Extraction Failed! Не вдалося видобути RomFS! - + There was an error copying the RomFS files or the user cancelled the operation. Під час копіювання файлів RomFS сталася помилка або користувач скасував операцію. - + Full Повний - + Skeleton Скелет - + Select RomFS Dump Mode Виберіть режим створення дампу RomFS - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. Виберіть, як ви хочете виконати дамп RomFS <br>Повний скопіює всі файли до нової теки, тоді як <br>скелет створить лише структуру тек. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root За адресою «%1» недостатньо вільного місця для видобування RomFS. Звільніть місце або виберіть іншу теку для створення дампу в «Емуляція» → «Налаштувати» → «Система» → «Файлова система» → «Коренева тека дампів». - + Extracting RomFS... Видобування RomFS... - - + + Cancel Скасувати - + RomFS Extraction Succeeded! RomFS видобуто успішно! - + The operation completed successfully. Операцію успішно виконано. - + Error Opening %1 Помилка під час відкриття «%1» - + Select Directory Вибрати теку - + Properties Властивості - + The game properties could not be loaded. Неможливо завантажити властивості гри. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Виконуваний файл Switch (%1);;Усі файли (*.*) - + Load File Завантажити файл - + Open Extracted ROM Directory Відкрити теку видобутого ROM - + Invalid Directory Selected Вибрано неправильну теку - + The directory you have selected does not contain a 'main' file. Вибрана тека не містить файлу «main». - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) Встановлюваний файл Switch (*.nca, *.nsp, *.xci);;Архів вмісту Nintendo (*.nca);;Пакет подання Nintendo (*.nsp);;Образ картриджа NX (*.xci) - + Install Files Встановити файли - + %n file(s) remaining Лишився 1 файлЛишилося %n файлиЛишилося %n файлівЛишилося %n файлів - + Installing file "%1"... Встановлення файлу «%1»... + - Install Results Результати встановлення - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. Щоб уникнути можливих конфліктів, ми не радимо користувачам встановлювати ігри в NAND. Користуйтеся цією функцією лише для встановлення оновлень і доповнень. - + %n file(s) were newly installed Щойно встановлено %n файл @@ -7722,7 +7738,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) were overwritten Перезаписано %n файл @@ -7732,7 +7748,7 @@ Please, only use this feature to install updates and DLC. - + %n file(s) failed to install Не вдалося встановити %n файл @@ -7742,314 +7758,324 @@ Please, only use this feature to install updates and DLC. - + System Application Системний застосунок - + System Archive Системний архів - + System Application Update Оновлення системного застосунку - + Firmware Package (Type A) Пакет прошивки (Тип А) - + Firmware Package (Type B) Пакет прошивки (Тип Б) - + Game Гра - + Game Update Оновлення гри - + Game DLC Доповнення гри - + Delta Title Проєкт «Дельта» - + Select NCA Install Type... Виберіть тип встановлення NCA... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) Виберіть тип проєкту, який ви хочете встановити для цього NCA: (У більшості випадків підходить стандартний вибір «Гра».) - + Failed to Install Не вдалося встановити - + The title type you selected for the NCA is invalid. Тип проєкту, який ви вибрали для NCA, неправильний. - + File not found Файл не виявлено - + File "%1" not found Файл «%1» не виявлено - + OK Гаразд - + Function Disabled Функцію вимкнену - + Compatibility list reporting is currently disabled. Check back later! Звітування для переліку сумісності наразі вимкнено. Зазирніть пізніше! - + Error opening URL Помилка під час відкриття URL - + Unable to open the URL "%1". Не вдалося відкрити URL: «%1». - + TAS Recording Записування TAS - + Overwrite file of player 1? Перезаписати файл гравця 1? - + Invalid config detected Виявлено неправильне налаштування - + Handheld controller can't be used on docked mode. Pro controller will be selected. Портативний контролер неможливо використовувати в режимі докстанції. Буде вибрано контролер Pro. - - + + Amiibo Amiibo - - + + The current amiibo has been removed Поточний amiibo вилучено - + Error Помилка - - + + The current game is not looking for amiibos Поточна гра не очікує amiibo - + Amiibo File (%1);; All Files (*.*) Файл amiibo (%1);; Усі файли (*.*) - + Load Amiibo Завантажити amiibo - + Error loading Amiibo data Помилка під час завантаження даних amiibo - + The selected file is not a valid amiibo Вибраний файл не є дійсним amiibo - + The selected file is already on use Вибраний файл уже використовується - + An unknown error occurred Сталася невідома помилка - + No firmware available Немає доступних прошивок - + Firmware Corrupted Прошивка пошкоджена - + Unknown applet Невідомий аплет - + Applet doesn't map to a known value. Аплет не призначено до відомого значення. - + Record not found Запис не виявлено - + Applet not found. Please reinstall firmware. Аплет не виявлено. Перевстановіть прошивку. - + Capture Screenshot Зробити знімок екрана - + PNG Image (*.png) Зображення PNG (*.png) - + TAS state: Running %1/%2 Стан TAS: Працює %1/%2 - + TAS state: Recording %1 Стан TAS: Триває запис %1 - + TAS state: Idle %1/%2 Стан TAS: Бездіяльність %1/%2 - + TAS State: Invalid Стан TAS: Неправильний - + &Stop Running [&S] Зупинити - + Stop R&ecording [&E] Зупинити запис - + Building: %n shader(s) Компіляція: %n шейдерКомпіляція: %n шейдериКомпіляція: %n шейдерівКомпіляція: %n шейдерів - + Scale: %1x %1 is the resolution scaling factor Масштаб: %1x - + Speed: %1% / %2% Швидкість: %1% / %2% - + Speed: %1% Швидкість: %1% - + Game: %1 FPS Гра: %1 к/с - + Frame: %1 ms Кадр: %1 мс - + NO AA БЕЗ ЗГЛАДЖУВАННЯ - + + NO FX + + + + + FX + + + + VOLUME: MUTE ГУЧНІСТЬ: ВИМКНЕНО - + VOLUME: %1% Volume percentage (e.g. 50%) ГУЧНІСТЬ: %1% - + Derivation Components Missing Відсутні компоненти виведення - + Decryption keys are missing. Install them now? Відсутні ключі шифрування. Встановити їх зараз? - + Wayland Detected! Виявлено Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8060,74 +8086,74 @@ Would you like to force it for future launches? Хочете примусово увімкнути його для наступних запусків? - + Use X11 Використовувати X11 - + Continue with Wayland Продовжити з Wayland - + Don't show again Не показувати знову - + Restart Required Потрібен перезапуск - + Restart Eden to apply the X11 backend. Перезапуск Eden для застосування бекенду X11. - + Slow Сповільнення - + Turbo Прискорення - + Unlocked Розблоковано - + Select RomFS Dump Target Виберіть розташування для створення дампу RomFS - + Please select which RomFS you would like to dump. Виберіть, який дамп RomFS ви хочете створити. - + Are you sure you want to close Eden? Ви впевнені, що хочете закрити Eden? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. Ви впевнені, що хочете зупинити емуляцію? Увесь незбережений поступ буде втрачено. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/vi.ts b/dist/languages/vi.ts index 2fc96b21fe..f0a7e591bd 100644 --- a/dist/languages/vi.ts +++ b/dist/languages/vi.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Chế độ toàn màn hình: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Tỉ lệ khung hình: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Giả lập NVDEC: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Chế độ Vsync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Bật hiển thị bất đồng bộ (chỉ cho Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Buộc chạy ở xung nhịp tối đa (chỉ cho Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Chạy các công việc trong nền trong khi đang chờ lệnh đồ họa để giữ cho GPU không giảm xung nhịp. - + Anisotropic Filtering: Lọc bất đẳng hướng: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Dùng bộ nhớ đệm pipeline Vulkan - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Bật xả tương ứng - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Đồng bộ hóa với tốc độ khung hình khi phát video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Chạy game với tốc độ bình thường trong quá trình phát video, ngay cả khi tốc độ khung hình được mở khóa. - + Barrier feedback loops Vòng lặp phản hồi rào cản - + Improves rendering of transparency effects in specific games. Cải thiện hiệu quả kết xuất của hiệu ứng trong suốt trong một số game. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Hạt giống RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Tên thiết bị - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Vùng: - + The region of the console. - + Time Zone: Múi giờ: - + The time zone of the console. - + Sound Output Mode: Chế độ đầu ra âm thanh: - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ẩn con trỏ chuột khi không dùng - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Vô hiệu hoá applet tay cầm - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) Không nén (Chất lượng tốt nhất) - + BC1 (Low quality) BC1 (Chất lượng thấp) - + BC3 (Medium quality) BC3 (Chất lượng trung bình) - + Auto Tự động - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - - + + Accurate Chính xác - - + + Default Mặc định - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3201,33 +3193,38 @@ Would you like to delete the old save data? Màu nền: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Tắt - + VSync Off Tắt Vsync - + Recommended Đề xuất - + On Bật - + VSync On Bật Vsync @@ -4849,102 +4846,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6959,7 +6971,7 @@ Debug Message: - + &Pause &Tạm dừng @@ -7110,7 +7122,7 @@ Debug Message: - + &Start &Bắt đầu @@ -7121,7 +7133,7 @@ Debug Message: - + R&ecord G&hi lại @@ -7212,7 +7224,7 @@ Debug Message: - + None @@ -7238,722 +7250,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7961,74 +7988,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/vi_VN.ts b/dist/languages/vi_VN.ts index e81053b393..51156ca9c8 100644 --- a/dist/languages/vi_VN.ts +++ b/dist/languages/vi_VN.ts @@ -571,70 +571,80 @@ FXAA can produce a more stable picture in lower resolutions. - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: Chế độ Toàn màn hình: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. - + Aspect Ratio: Tỉ lệ khung hình: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: Giả lập NVDEC - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. - + ASTC Decoding Method: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -643,55 +653,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: Chế độ Vsync: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -699,426 +729,392 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) Bật hiển thị bất đồng bộ (chỉ dành cho Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. - + Force maximum clocks (Vulkan only) Buộc chạy ở xung nhịp tối đa (chỉ Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. Chạy các công việc trong nền trong khi đang chờ lệnh đồ họa để giữ cho GPU không giảm xung nhịp. - + Anisotropic Filtering: Bộ lọc góc nghiêng: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache Dùng Vulkan pipeline cache - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. - + Enable Compute Pipelines (Intel Vulkan Only) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing Bật xả tương ứng - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. - + Sync to framerate of video playback Đồng bộ hóa với tốc độ khung hình khi phát video - + Run the game at normal speed during video playback, even when the framerate is unlocked. Chạy game với tốc độ bình thường trong quá trình phát video, ngay cả khi tốc độ khung hình được mở khóa. - + Barrier feedback loops Vòng lặp phản hồi rào cản - + Improves rendering of transparency effects in specific games. Cải thiện hiệu quả hiển thị của hiệu ứng trong suốt trong một số trò chơi. - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed Hạt giống RNG - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name Tên thiết bị - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: - + This option can be overridden when region setting is auto-select - + Region: Vùng: - + The region of the console. - + Time Zone: Múi giờ: - + The time zone of the console. - + Sound Output Mode: Chế độ đầu ra âm thanh - + Console Mode: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity Ẩn con trỏ chuột khi không dùng - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - Vô hiệu hoá applet tay cầm - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1150,155 +1146,156 @@ When a program attempts to open the controller applet, it is immediately closed. - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU - + CPU Asynchronous - + Uncompressed (Best quality) Không nén (Chất lượng tốt nhất) - + BC1 (Low quality) BC1 (Chất lượng thấp) - + BC3 (Medium quality) BC3 (Chất lượng trung bình) - + Auto Tự động - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative - + Aggressive - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null Null - + Fast - - + + Accurate Tuyệt đối - - + + Default Mặc định - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2075,11 +2072,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3201,33 +3193,38 @@ Would you like to delete the old save data? Màu nền: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off Tắt - + VSync Off Tắt Vsync - + Recommended Đề xuất - + On Bật - + VSync On Bật Vsync @@ -4849,102 +4846,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6959,7 +6971,7 @@ Debug Message: - + &Pause &Tạm dừng @@ -7110,7 +7122,7 @@ Debug Message: - + &Start &Bắt đầu @@ -7121,7 +7133,7 @@ Debug Message: - + R&ecord G&hi @@ -7212,7 +7224,7 @@ Debug Message: - + None @@ -7238,722 +7250,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7961,74 +7988,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts index 2321ff89e0..2b86a73843 100644 --- a/dist/languages/zh_CN.ts +++ b/dist/languages/zh_CN.ts @@ -594,12 +594,22 @@ SMAA 提供最佳质量。 FXAA 可以在较低分辨率下产生更稳定的画面。 - + + Enable post-processing effects + 开启后处理效果 + + + + Applies post-processing effects to the final image. + 给最终图像加上后处理效果。 + + + Fullscreen Mode: 全屏模式: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -608,12 +618,12 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp 独占全屏提供更好的性能和 Freesync/Gsync 支持。 - + Aspect Ratio: 屏幕纵横比: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. @@ -622,36 +632,36 @@ Also controls the aspect ratio of captured screenshots. 此设置同时控制捕获截图的高宽比。 - + Use persistent pipeline cache 使用持久管道缓存 - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. 将生成的着色器保存到硬盘,提高后续游戏过程中的着色器加载速度。 请仅在调试时禁用此项。 - + Use asynchronous GPU emulation 使用异步 GPU 模拟 - + Uses an extra CPU thread for rendering. This option should always remain enabled. 使用额外的 CPU 线程进行渲染。 此选项应始终保持启用状态。 - + NVDEC emulation: NVDEC 模拟方式: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -660,12 +670,12 @@ In most cases, GPU decoding provides the best performance. 大多数情况下,使用 GPU 解码将提供最好的性能。 - + ASTC Decoding Method: ASTC 纹理解码方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -678,12 +688,12 @@ CPU 异步模拟:使用 CPU 在 ASTC 纹理到达时对其进行解码。 消除 ASTC 解码带来的卡顿,但在解码时可能出现渲染问题。 - + ASTC Recompression Method: ASTC 纹理重压缩方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. @@ -692,44 +702,64 @@ BC1/BC3: 中间格式将被重新压缩为 BC1 或 BC3 格式,从而节省显存 但会降低图像质量。 - + Frame Pacing Mode (Vulkan only) 帧同步模式 (仅限 Vulkan) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. 控制模拟器如何管理帧同步,以减少卡顿,使帧率表现更加平稳顺滑。 - + VRAM Usage Mode: VRAM 使用模式: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. 选择模拟器是应优先节省内存还是最大限度地使用可用视频内存以提高性能。 激进模式可能会影响诸如录屏软件等其他应用程序的性能,。 - + Skip CPU Inner Invalidation 跳过 CPU 内部失效处理 - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. 在内存更新期间跳过某些缓存失效,从而降低 CPU 使用率并改善延迟。这可能导致软件崩溃。 - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: 垂直同步模式: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -740,12 +770,12 @@ Mailbox 的延迟可能比 FIFO 低且不会导致撕裂,但可能会丢帧。 Immediate (不同步) 会呈现全部可用内容,并可能出现撕裂。 - + Sync Memory Operations 同步内存操作 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. @@ -754,44 +784,44 @@ Unreal Engine 4 games often see the most significant changes thereof. 虚幻 4 引擎的游戏通常会看到最显著的变化。 - + Enable asynchronous presentation (Vulkan only) 启用异步帧提交 (仅限 Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. 将帧提交移动到单独的 CPU 线程,略微提高性能。 - + Force maximum clocks (Vulkan only) 强制最大时钟 (仅限 Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. 在后台运行的同时等待图形命令,以防止 GPU 降低时钟速度。 - + Anisotropic Filtering: 各向异性过滤: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. 控制在斜角下纹理渲染的质量。 大多数 GPU 上设置为 16 倍是安全的。 - + GPU Mode: GPU 模式: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. @@ -799,12 +829,12 @@ Particles tend to only render correctly with Accurate mode. 大多数游戏在“快速”模式下渲染效果良好,但有些游戏仍需使用“精确”模式。而粒子效果通常只有在“精确”模式下才能正确渲染。 - + DMA Accuracy: DMA 精度: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. @@ -813,75 +843,57 @@ Default follows the GPU Accuracy setting. 默认使用 GPU 精度设置。 - + GPU Fence Behavior: GPU 围栏行为: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. - 控制 GPU 围栏同步行为。 -“即时”是速度最快的选项,但可能会引入一些问题。 -“均衡”提供了更好的兼容性,可能修复某些游戏中的问题。 -“精确”在牺牲部分性能的前提下进一步提升兼容性。 -“严格”是速度最慢的选项,但可以修复那些要求更严格同步的问题。 -默认遵循 GPU “精确”设定。 +Default follows the GPU Mode setting. + - - Enable GPU buffer readback - 开启 GPU 缓冲区回读 - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - 通过在上传前读取来保留 GPU 修改的数据。 -某些游戏需要这样才能正确渲染某些效果。 - - - + Enable asynchronous shader compilation 开启异步着色器编译 - + May reduce shader stutter. 可能减少着色器卡顿。 - + GPU Clocks GPU 时钟频率 - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. 通过让游戏误以为 GPU 的处理速度比实际更快,以避免游戏为了适应 Switch 的时钟频率而降低分辨率和渲染距离。 - + GPU Unswizzle GPU 还原 - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. 利用 GPU 计算来加速 BCn 格式 3D 纹理的解码。如果遇到崩溃或画面花屏,请禁用此项。 - + GPU Unswizzle Max Texture Size GPU 还原最大纹理尺寸 - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. @@ -890,48 +902,48 @@ Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size GPU 还原流大小 - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. 设置每帧处理的纹理数据最大量(单位:MiB)。 较高的数值可以减少纹理加载时的卡顿,但可能会影响帧率的稳定性(即造成帧时间波动)。 - + GPU Unswizzle Chunk Size GPU 还原块大小 - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. 确定在单次调度(Dispatch)中处理的深度切片(Depth Slices)数量。 增加此数值可以提高高端 GPU 的吞吐量(处理效率),但在性能较弱的硬件上可能会引发 TDR(驱动程序重置)或驱动超时。 - + Use Vulkan pipeline cache 启用 Vulkan 管线缓存 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. 启用 GPU 供应商专用的管线缓存。 在 Vulkan 驱动程序内部不存储管线缓存的情况下,此选项可显著提高着色器加载速度。 - + Enable Compute Pipelines (Intel Vulkan Only) 启用计算管线 (仅限 Intel 显卡 Vulkan 模式) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. @@ -940,193 +952,181 @@ Compute pipelines are always enabled on all other drivers. 在所有其他驱动程序上始终启用计算管线。 - + Enable Reactive Flushing 启用反应性刷新 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. 使用反应性刷新取代预测性刷新,从而更精确地同步内存。 - + Sync to framerate of video playback 播放视频时帧率同步 - + Run the game at normal speed during video playback, even when the framerate is unlocked. 在视频播放期间以正常速度运行游戏,即使帧率未锁定。 - + Barrier feedback loops 屏障反馈环路 - + Improves rendering of transparency effects in specific games. 改进某些游戏中透明效果的渲染。 - + Enable buffer history 启用缓冲区历史 - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. 允许访问之前的缓冲状态。 这个选项可能会提升某些游戏的渲染质量和性能一致性。 - + Fix bloom effects 修复泛光效果 - + Removes bloom in Burnout. 去除《火爆狂飙》中的泛光特效。 - - Enable Legacy Rescale Pass - 启用旧版缩放 - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - 通过依赖之前实现的行为,可能会修复部分游戏的缩放重叠问题。 -修复 AMD 和 Intel 显卡上的线条伪影,以及《路易斯洋楼3》中 Nvidia 显卡的灰色纹理闪烁的遗留行为变通方法。 - - - + Extended Dynamic State 扩展动态状态 - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. 控制在扩展动态状态中可使用的函数数量。更高的数值允许启用更多功能,并可能提升性能,但同时也可能导致额外的图形问题。 - + Vertex Input Dynamic State 顶点输入动态状态 - + Enables vertex input dynamic state feature for better quality and performance. 开启顶点输入动态状态功能来获得更好的质量和性能。 - + Sample Shading 采样着色 - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. 允许片段着色器在多重采样的片段中每个样本执行一次而不是每个片段执行一次。可以提高图形质量,但会降低性能。 更高的值可以提高质量但会降低性能。 - + RNG Seed 随机数生成器种子 - + Controls the seed of the random number generator. Mainly used for speedrunning. 控制随机数生成器的种子。 主要用于竞速游戏。 - + Device Name 设备名称 - + The name of the console. 主机的数量。 - + Homebrew Args 自制软件参数 - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). 启动 homebrew 时需要传递的命令行参数 (例如 -noglsl)。 - + Custom RTC Date: 自定义系统时间: - + This option allows to change the clock of the console. Can be used to manipulate time in games. 此选项允许更改控制台的时钟。 可用于操纵游戏中的时间。 - + The number of seconds from the current unix time 来自当前 unix 时间的秒数。 - + Language: 语言: - + This option can be overridden when region setting is auto-select 当区域设置为自动选择时可以使用此选项替代。 - + Region: 地区: - + The region of the console. 主机的区域。 - + Time Zone: 时区: - + The time zone of the console. 主机的时区。 - + Sound Output Mode: 声音输出模式: - + Console Mode: 控制台模式: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. @@ -1135,59 +1135,47 @@ Setting to Handheld can help improve performance for low end systems. 将设置为掌机模式可以帮助低端系统提高性能。 - + Prompt for user profile on boot 启动时提示选择用户账户 - + Useful if multiple people use the same PC. 在多人使用相同的 PC 时有效。 - + Pause when not in focus 在丢失焦点时暂停 - + Pauses emulation when focusing on other windows. 当焦点位于其它窗口时暂停模拟器。 - + Confirm before stopping emulation 停止模拟时需要确认 - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. 替代提示以确认停止模拟。 启用它将绕过此类提示并直接退出模拟。 - + Hide mouse on inactivity 自动隐藏鼠标光标 - + Hides the mouse after 2.5s of inactivity. 在 2.5 秒无活动后隐藏鼠标。 - - - Disable controller applet - 禁用控制器小程序 - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - 强制禁止在模拟程序中使用控制器小程序。 - 当程序尝试打开控制器小程序时,它会立即被关闭。 - Check for updates @@ -1219,155 +1207,156 @@ When a program attempts to open the controller applet, it is immediately closed. 真实的小程序 - + + + Disabled + 禁用 + + + Never 永不 - + On Load 加载时 - + Always 总是 - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 异步模拟 - + Uncompressed (Best quality) 不压缩 (最高质量) - + BC1 (Low quality) BC1 (低质量) - + BC3 (Medium quality) BC3 (中等质量) - + Auto 自动 - + 30 FPS 30 FPS - + 60 FPS 60 FPS - + 90 FPS 90 FPS - + 120 FPS 120 FPS - + Conservative 保守模式 - + Aggressive 激进模式 - + Vulkan Vulkan - + OpenGL GLSL OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) OpenGL GLASM (汇编着色器,仅限 NVIDIA 显卡) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) OpenGL SPIR-V (实验性,仅限 AMD/Mesa) - + Null 无 - + Fast 高速 - - + + Accurate 高精度 - - + + Default 系统默认 - + Unsafe (fast) 不安全(快速) - + Safe (stable) 安全(稳定) - + Immediate 即时 - + Balanced 平衡 - - - Strict - 严格 - Unsafe @@ -2144,11 +2133,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) 高(512) - - - Disabled - 禁用 - ExtendedDynamicState 1 @@ -3270,33 +3254,38 @@ Would you like to delete the old save data? 背景颜色: - + % FSR/SGSR sharpening percentage (e.g. 50%) % - + + Configure Effects... + 配置效果... + + + Off 关闭 - + VSync Off 垂直同步关 - + Recommended 推荐 - + On 开启 - + VSync On 垂直同步开 @@ -4918,108 +4907,124 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t Post-Processing Effects - + 后处理效果 - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. - + Changes apply immediately while a game is running. + 更改会在游戏运行时立即生效。 - + Enable post-processing - + 开启后处理 - + Preset: - + 预设: - + Apply - + 应用 - + Reset Values - + 重置值 - + Return every effect to the values the preset ships with. - + 把每个效果恢复到预设的默认值 - + Save As... - + 另存为... - + Save Preset - + 保存预设 - + Preset name: - + 预设名称: - - Delete - + + Clear + 清除 - + + Remove every effect in use and deselect the preset. + 移除所有使用中的效果并取消选择预设。 + + + Add Effect - + 添加效果 - - Custom chain. Pick a preset above and press Apply to replace it. - + + None + 无 + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + 没有使用中的效果。请选择一个预设并点击应用,或一个一个地添加效果。 + Custom chain. Pick a preset above and press Apply to replace it. + 自定义链。选择上面的预设并点击应用替换它。 + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + 活动的预设: %1 (已修改)。按重置值可以恢复出厂设置。 - + Active preset: %1 - + 活动的预设: %1 - + Enabled - + 开启 - + Disabled - + 关闭 - + Reset to defaults - + 重置为默认值 - + Effect '%1' was not found. - + 找不到效果 '%1'。 - + Effect failed to compile: %1 - + 编译效果失败: +%1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. - + 找不到可用的 ReShade FX 效果。请将 .fx 文件放到 post_shaders 文件夹中。 @@ -7033,7 +7038,7 @@ Debug Message: - + &Pause 暂停 (&P) @@ -7135,7 +7140,7 @@ Debug Message: &User Handbook - + 用户手册(&U) @@ -7184,7 +7189,7 @@ Debug Message: - + &Start 开始 (&S) @@ -7195,7 +7200,7 @@ Debug Message: - + R&ecord 录制 (&E) @@ -7286,7 +7291,7 @@ Debug Message: - + None 无 @@ -7304,7 +7309,7 @@ Debug Message: Post-Processing &Shaders... - + 后处理效果着色器(&S)... @@ -7312,727 +7317,742 @@ Debug Message: 轮播视图(&C) - + Small (32x32) 小 (32x32) - + Standard (64x64) 标准 (64x64) - + Large (128x128) 大 (128x128) - + Full Size (256x256) 最大 (256x256) - + Broken Vulkan Installation Detected 检测到损坏的 Vulkan 安装 - + Vulkan initialization failed during boot. 在启动时初始化 Vulkan 失败。 - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping 运行游戏 - + Loading Web Applet... 正在加载 Web 小程序... - - + + Disable Web Applet 禁用 Web 小程序 - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) 禁用网页小程序可能会导致未定义的行为并且应仅在 超级马里奥 3D 全明星中使用。您确定要禁用网页小程序吗? (这可以在调试设置中重新启用。) - + The amount of shaders currently being built 当前正在构建的着色器数量 - + The current selected resolution scaling multiplier. 当前选择的分辨率缩放倍数。 - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. 当前模拟速度。高于或低于 100% 的数值表示模拟运行比 Switch 快或慢。 - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. 游戏当前显示的每秒帧数。这个数值会因游戏和场景的不同而有所变化。 - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. 模拟 Switch 一帧所需的时间,不包括帧限制或垂直同步。为了全速模拟这个时间最多应为 16.67 毫秒。 - + Unmute 取消静音 - + Mute 静音 - + Reset Volume 重置音量 - + + Configure Effects... + 配置效果... + + + &Clear Recent Files 清除最近的文件(&C) - + &Continue 继续(&C) - + Warning: Outdated Game Format 警告: 游戏格式过时 - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. 您正在为此游戏使用解包 ROM 目录格式,这是一种已过时的格式,它已被 NCA、NAX、XCI 或 NSP 等其他格式取代。解包 ROM 目录缺少图标、元数据和更新支持。<br>有关 Eden 支持的各种 Switch 格式的说明请查阅我们的用户手册。此消息将不再显示。 - - + + Error while loading ROM! 加载 ROM 时出错! - + The ROM format is not supported. 不支持该 ROM 格式。 - + An error occurred initializing the video core. 初始化视频核心时发生错误。 - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + 这通常是因为过时的 GPU 驱动所导致的。具体情况请查看日志。请查阅: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>如何访问日志文件</a>。 - + Error while loading ROM! %1 %1 signifies a numeric error code. 加载 ROM 时出错! %1 - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. %1<br>请重新导出您的文件,或在 Discord/Stoat 上寻求帮助。 - + An unknown error occurred. Please see the log for more details. 发生未知错误。请查看日志以获取更多详情。 - - + + (64-bit) (64 位) - - + + (32-bit) (32 位) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit %1 %2 - + Closing software... 正在关闭软件... - + Save Data 存档数据 - + Mod Data Mod 数据 - + Error Opening %1 Folder 打开 %1 文件夹出错 - - + + Folder does not exist! 文件夹不存在! - + Remove Installed Game Contents? 是否移除已安装的游戏内容? - + Remove Installed Game Update? 是否移除已安装的游戏更新? - + Remove Installed Game DLC? 是否移除已安装的游戏 DLC? - + Remove Entry 删除条目 - + Delete OpenGL Transferable Shader Cache? 要删除 OpenGL 可传输着色器缓存吗? - + Delete Vulkan Transferable Shader Cache? 要删除 Vulkan 可传输着色器缓存吗? - + Delete All Transferable Shader Caches? 删除所有可传输的着色器缓存? - + Remove Custom Game Configuration? 是否移除自定义游戏配置? - + Remove Cache Storage? 要清除缓存存储吗? - + Remove File 删除文件 - + Remove Play Time Data 删除游戏时间数据 - + Reset play time? 要重置播放时间吗? - - + + RomFS Extraction Failed! RomFS 提取失败! - + There was an error copying the RomFS files or the user cancelled the operation. 复制 RomFS 文件时出错或用户取消了操作。 - + Full 完整 - + Skeleton 结构 - + Select RomFS Dump Mode 选择 RomFS 转储模式 - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. 请选择您希望如何导出 RomFS。<br>&quot;完整&quot; 将把所有文件复制到新的目录中,而<br>&quot;结构&quot; 仅会创建目录结构。</br></br> - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root %1 的可用空间不足,无法提取 RomFS。请释放空间或在模拟 > 配置 > 系统 > 文件系统 > 转储根目录,中选择其它目录 - + Extracting RomFS... 正在提取 RomFS... - - + + Cancel 取消 - + RomFS Extraction Succeeded! RomFS 提取成功! - + The operation completed successfully. 操作已成功完成。 - + Error Opening %1 打开 %1 时出错 - + Select Directory 选择目录 - + Properties 属性 - + The game properties could not be loaded. 无法加载游戏属性。 - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. Switch 可执行文件 (%1);;所有文件 (*.*) - + Load File 加载文件 - + Open Extracted ROM Directory 打开已提取的 ROM 目录 - + Invalid Directory Selected 选择的目录无效 - + The directory you have selected does not contain a 'main' file. 您选择的目录不包含 'main' 文件。 - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) 可安装的 Switch 文件 (*.nca *.nsp *.xci);;任天堂内容档案 (*.nca);;任天堂提交包 (*.nsp);;NX 卡带镜像 (*.xci) - + Install Files 安装文件 - + %n file(s) remaining 剩余 %n 个文件 - + Installing file "%1"... 正在安装文件 "%1"... + - Install Results 安装结果 - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. 为了避免可能的冲突,我们不鼓励用户将基础游戏安装到 NAND。 请仅使用此功能来安装更新和 DLC。 - + %n file(s) were newly installed 已新安装 %n 个文件 - + %n file(s) were overwritten 已覆盖了 %n 个文件 - + %n file(s) failed to install %n 个文件安装失败 - + System Application 系统应用 - + System Archive 系统档案 - + System Application Update 系统应用更新 - + Firmware Package (Type A) 固件包 (类型 A) - + Firmware Package (Type B) 固件包 (类型 B) - + Game 游戏 - + Game Update 游戏更新 - + Game DLC 游戏可下载内容 - + Delta Title Delta 标题 - + Select NCA Install Type... 选择 NCA 安装类型... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) 请选择您希望将此 NCA 安装为的标题类型: (在大多数情况下,默认的 '游戏' 就可以。) - + Failed to Install 安装失败 - + The title type you selected for the NCA is invalid. 您为 NCA 选择的标题类型无效。 - + File not found 找不到文件 - + File "%1" not found 未找到文件 "%1" - + OK 确定 - + Function Disabled 功能已被关闭 - + Compatibility list reporting is currently disabled. Check back later! 兼容性列表报告目前已被禁用。请稍后再查看! - + Error opening URL 打开网址出错 - + Unable to open the URL "%1". 无法打开 URL "%1"。 - + TAS Recording TAS 录像 - + Overwrite file of player 1? 要覆盖玩家 1 的文件吗? - + Invalid config detected 检测到无效配置 - + Handheld controller can't be used on docked mode. Pro controller will be selected. 手柄在主机模式下无法使用。将选择 Pro 手柄。 - - + + Amiibo Amiibo - - + + The current amiibo has been removed 当前的 amiibo 已被移除 - + Error 错误 - - + + The current game is not looking for amiibos 当前游戏不支持寻找 amiibo - + Amiibo File (%1);; All Files (*.*) Amiibo 文件 (%1);; 所有文件 (*.*) - + Load Amiibo 读取 Amiibo - + Error loading Amiibo data 加载 Amiibo 数据出错 - + The selected file is not a valid amiibo 所选文件不是有效的 amiibo - + The selected file is already on use 所选文件正在使用中 - + An unknown error occurred 发生未知错误 - + No firmware available 没有可用的固件 - + Firmware Corrupted 固件已损坏 - + Unknown applet 未知小程序 - + Applet doesn't map to a known value. 无法识别该小程序对应的值。 - + Record not found 找不到记录程序 - + Applet not found. Please reinstall firmware. 找不到小程序。请重新安装固件。 - + Capture Screenshot 截取屏幕截图 - + PNG Image (*.png) PNG 图像 (*.png) - + TAS state: Running %1/%2 TAS 状态: 正在运行 %1/%2 - + TAS state: Recording %1 TAS 状态: 正在录制 %1 - + TAS state: Idle %1/%2 TAS 状态: 空闲 %1/%2 - + TAS State: Invalid TAS 状态: 无效 - + &Stop Running 停止运行(&S) - + Stop R&ecording 停止录制(&A) - + Building: %n shader(s) 正在编译:%n 个着色器 - + Scale: %1x %1 is the resolution scaling factor 缩放: %1x - + Speed: %1% / %2% 速度: %1% / %2% - + Speed: %1% 速度: %1% - + Game: %1 FPS 游戏: %1 FPS - + Frame: %1 ms 帧: %1 ms - + NO AA 无 AA - + + NO FX + 无 FX + + + + FX + FX + + + VOLUME: MUTE 音量: 静音 - + VOLUME: %1% Volume percentage (e.g. 50%) 音量: %1% - + Derivation Components Missing 缺少派生组件 - + Decryption keys are missing. Install them now? 缺少解密密钥。现在安装吗? - + Wayland Detected! 检测到 Wayland! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -8043,74 +8063,74 @@ Would you like to force it for future launches? 您想要在未来的启动中强制执行吗? - + Use X11 使用 X11 - + Continue with Wayland 继续使用 Wayland - + Don't show again 不再显示 - + Restart Required 需要重新启动 - + Restart Eden to apply the X11 backend. 重新启动 Eden 以应用 X11 后端。 - + Slow 慢速 - + Turbo 加速 - + Unlocked 解锁 - + Select RomFS Dump Target 选择 RomFS 转储目标 - + Please select which RomFS you would like to dump. 请选择您想要转储的 RomFS。 - + Are you sure you want to close Eden? 您确实要关闭 Eden 吗? - - - + + + Eden Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. 您确定要停止模拟吗?任何未保存的进度将会丢失。 - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? @@ -9313,7 +9333,7 @@ p, li { white-space: pre-wrap; } This game may require firmware to function properly, and you don't have any installed. Please install firmware before launching, or press "OK" to launch anyways. - + 这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。 diff --git a/dist/languages/zh_TW.ts b/dist/languages/zh_TW.ts index 3aa6892ac9..463d7fa2ed 100644 --- a/dist/languages/zh_TW.ts +++ b/dist/languages/zh_TW.ts @@ -592,12 +592,22 @@ SMAA的品質最佳 FXAA在低解析度下可以產生較穩定的畫面 - + + Enable post-processing effects + + + + + Applies post-processing effects to the final image. + + + + Fullscreen Mode: 全螢幕模式: - + The method used to render the window in fullscreen. Borderless offers the best compatibility with the on-screen keyboard that some games request for input. Exclusive fullscreen may offer better performance and better Freesync/Gsync support. @@ -606,47 +616,47 @@ Exclusive fullscreen may offer better performance and better Freesync/Gsync supp 獨佔全螢幕將提供更好的性能以及更佳的FreeSync/G-Sync支援 - + Aspect Ratio: 長寬比: - + Stretches the renderer to fit the specified aspect ratio. Most games only support 16:9, so modifications are required to get other ratios. Also controls the aspect ratio of captured screenshots. - + Use persistent pipeline cache - + Allows saving shaders to storage for faster loading on following game boots. Disabling it is only intended for debugging. 將產生的著色器快取儲存至硬碟,讓遊戲在後續過程中不需再次產生以提高速度 建議僅在偵錯時才停用此選項 - + Use asynchronous GPU emulation - + Uses an extra CPU thread for rendering. This option should always remain enabled. - + NVDEC emulation: NVDEC 模擬: - + Specifies how videos should be decoded. It can either use the CPU or the GPU for decoding, or perform no decoding at all (black screen on videos). In most cases, GPU decoding provides the best performance. @@ -655,12 +665,12 @@ In most cases, GPU decoding provides the best performance. GPU解碼在大多數情況下提供最好的性能 - + ASTC Decoding Method: ASTC解碼方式: - + This option controls how ASTC textures should be decoded. CPU: Use the CPU for decoding. GPU: Use the GPU's compute shaders to decode ASTC textures (recommended). @@ -669,55 +679,75 @@ stuttering but may present artifacts. - + ASTC Recompression Method: ASTC重新壓縮方式: - + Most GPUs lack support for ASTC textures and must decompress to anintermediate format: RGBA8. BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format, saving VRAM but degrading image quality. - + Frame Pacing Mode (Vulkan only) - + Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent. - + VRAM Usage Mode: VRAM 使用模式: - + Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance. Aggressive mode may impact performance of other applications such as recording software. - + Skip CPU Inner Invalidation 跳過CPU內部失效處理 - + Skips certain cache invalidations during memory updates, reducing CPU usage and improving latency. This may cause soft-crashes. - + + GPU Readback Before NCE Invalidation + + + + + Flushes GPU buffer data back to guest memory before NCE invalidates the whole guest page, preserving it while the CPU store proceeds. + + + + + Patch Runtime NROs for NCE + + + + + Applies NCE code patches to runtime-loaded NRO modules.This is required for some games to run correctly. + + + + VSync Mode: 垂直同步: - + FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. FIFO Relaxed allows tearing as it recovers from a slow down. Mailbox can have lower latency than FIFO and does not tear but may drop frames. @@ -725,427 +755,393 @@ Immediate (no synchronization) presents whatever is available and can exhibit te - + Sync Memory Operations 同步記憶體操作 - + Ensures data consistency between compute and memory operations. This option fixes issues in games, but may degrade performance. Unreal Engine 4 games often see the most significant changes thereof. - + Enable asynchronous presentation (Vulkan only) 啟用非同步顯示(僅限Vulkan) - + Slightly improves performance by moving presentation to a separate CPU thread. 透過將畫面顯示移至獨立的CPU執行緒來略微提升性能。 - + Force maximum clocks (Vulkan only) 強制使用最大時脈(僅限Vulkan) - + Runs work in the background while waiting for graphics commands to keep the GPU from lowering its clock speed. 在等待圖形命令時於背景執行任務以防GPU降低時脈 - + Anisotropic Filtering: 各向異性過濾: - + Controls the quality of texture rendering at oblique angles. Safe to set at 16x on most GPUs. - + GPU Mode: - + Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode. - + DMA Accuracy: - + Controls the DMA read mode. Unsafe is faster, while Safe is more stable and can fix issues in some games. Default follows the GPU Accuracy setting. - + GPU Fence Behavior: - + Controls the GPU fence synchronization behavior. Immediate is the fastest option, but can introduce some issues. Balanced offers better compatibility and may fix issues in some games. Accurate further improves compatibility at the cost of some performance. -Strict is the slowest option, but can fix issues that require stricter synchronization. -Default follows the GPU Accuracy setting. +Default follows the GPU Mode setting. - - Enable GPU buffer readback - - - - - Preserves GPU-modified data by reading it back before uploading. -Some games require this to render certain effects properly. - - - - + Enable asynchronous shader compilation - + May reduce shader stutter. - + GPU Clocks - + Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch's clocks. - + GPU Unswizzle - + Accelerates BCn 3D texture decoding using GPU compute. Disable if experiencing crashes or graphical glitches. - + GPU Unswizzle Max Texture Size - + Sets the maximum size (MiB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. - + GPU Unswizzle Stream Size - + Sets the maximum amount of texture data (in MiB) processed per frame. Higher values can reduce stutter during texture loading but may impact frame consistency. - + GPU Unswizzle Chunk Size - + Determines the number of depth slices processed in a single dispatch. Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware. - + Use Vulkan pipeline cache 启用 Vulkan 管线缓存 - + Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally. 启用 GPU 供应商专用的管线缓存。 在 Vulkan 驱动程序内部不存储管线缓存的情况下,此选项可显著提高着色器加载速度。 - + Enable Compute Pipelines (Intel Vulkan Only) 启用计算管线 (仅限 Intel 显卡 Vulkan 模式) - + Required by some games. This setting only exists for Intel proprietary drivers and may crash if enabled. Compute pipelines are always enabled on all other drivers. - + Enable Reactive Flushing 启用反应性刷新 - + Uses reactive flushing instead of predictive flushing, allowing more accurate memory syncing. 使用反应性刷新取代预测性刷新,从而更精确地同步内存。 - + Sync to framerate of video playback 播放视频时帧率同步 - + Run the game at normal speed during video playback, even when the framerate is unlocked. 在视频播放期间以正常速度运行游戏,即使帧率未锁定。 - + Barrier feedback loops 屏障反馈循环 - + Improves rendering of transparency effects in specific games. 改进某些游戏中透明效果的渲染。 - + Enable buffer history - + Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - + Fix bloom effects - + Removes bloom in Burnout. - - Enable Legacy Rescale Pass - - - - - May fix rescale issues in some games by relying on behavior from the previous implementation. -Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3. - - - - + Extended Dynamic State - + Controls the number of features that can be used in Extended Dynamic State. Higher states allow for more features and can increase performance, but may cause additional graphical issues. - + Vertex Input Dynamic State - + Enables vertex input dynamic state feature for better quality and performance. - + Sample Shading - + Allows the fragment shader to execute per sample in a multi-sampled fragment instead of once per fragment. Improves graphics quality at the cost of performance. Higher values improve quality but degrade performance. - + RNG Seed 隨機種子 - + Controls the seed of the random number generator. Mainly used for speedrunning. - + Device Name 裝置名稱 - + The name of the console. - + Homebrew Args - + Command-line arguments passed to homebrew at launch (e.g. -noglsl). - + Custom RTC Date: 自定义系统时间: - + This option allows to change the clock of the console. Can be used to manipulate time in games. - + The number of seconds from the current unix time - + Language: 语言: - + This option can be overridden when region setting is auto-select - + Region: 區域: - + The region of the console. - + Time Zone: 時區: - + The time zone of the console. - + Sound Output Mode: 音訊輸出模式: - + Console Mode: 控制台模式: - + Selects if the console is in Docked or Handheld mode. Games will change their resolution, details and supported controllers and depending on this setting. Setting to Handheld can help improve performance for low end systems. - + Prompt for user profile on boot - + Useful if multiple people use the same PC. - + Pause when not in focus - + Pauses emulation when focusing on other windows. - + Confirm before stopping emulation 停止模拟时需要确认 - + Overrides prompts asking to confirm stopping the emulation. Enabling it bypasses such prompts and directly exits the emulation. - + Hide mouse on inactivity 滑鼠閒置時自動隱藏 - + Hides the mouse after 2.5s of inactivity. - - - Disable controller applet - 禁用控制器程序 - - - - Forcibly disables the use of the controller applet in emulated programs. -When a program attempts to open the controller applet, it is immediately closed. - - Check for updates @@ -1177,155 +1173,156 @@ When a program attempts to open the controller applet, it is immediately closed. 真实的小程序 - - Never + + + Disabled - On Load + Never + On Load + + + + Always - + CPU CPU - + GPU GPU - + CPU Asynchronous CPU 异步模拟 - + Uncompressed (Best quality) 不壓縮 (最高品質) - + BC1 (Low quality) BC1 (低品質) - + BC3 (Medium quality) BC3 (中品質) - + Auto 自動 - + 30 FPS - + 60 FPS - + 90 FPS - + 120 FPS - + Conservative 保守模式(节省 VRAM) - + Aggressive 激进模式 - + Vulkan Vulkan - + OpenGL GLSL - + OpenGL GLASM (Assembly Shaders, NVIDIA Only) - + OpenGL SPIR-V (Experimental, AMD/Mesa Only) - + Null 無 - + Fast - - + + Accurate 高精度 - - + + Default 預設 - + Unsafe (fast) - + Safe (stable) - + Immediate - + Balanced - - - Strict - - Unsafe @@ -2102,11 +2099,6 @@ When a program attempts to open the controller applet, it is immediately closed. High (512) - - - Disabled - - ExtendedDynamicState 1 @@ -3227,33 +3219,38 @@ Would you like to delete the old save data? 背景顏色: - + % FSR/SGSR sharpening percentage (e.g. 50%) - + + Configure Effects... + + + + Off 關閉 - + VSync Off 垂直同步關 - + Recommended 推薦 - + On 開啟 - + VSync On 垂直同步開 @@ -4875,102 +4872,117 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t - ReShade FX effects are loaded from the post_shaders folder in the Eden data directory. Changes apply immediately while a game is running. + Changes apply immediately while a game is running. - + Enable post-processing - + Preset: - + Apply - + Reset Values - + Return every effect to the values the preset ships with. - + Save As... - + Save Preset - + Preset name: - - Delete + + Clear - + + Remove every effect in use and deselect the preset. + + + + Add Effect - - Custom chain. Pick a preset above and press Apply to replace it. + + None + + + + + No effects in use. Pick a preset and press Apply, or add effects one by one. + Custom chain. Pick a preset above and press Apply to replace it. + + + + Active preset: %1 (modified). Press Reset Values to go back to how it ships. - + Active preset: %1 - + Enabled - + Disabled - + Reset to defaults - + Effect '%1' was not found. - + Effect failed to compile: %1 - + No usable ReShade FX effects were found. Place .fx files in the post_shaders folder. @@ -6984,7 +6996,7 @@ Debug Message: - + &Pause 暫停(&P) @@ -7135,7 +7147,7 @@ Debug Message: - + &Start 開始(&S) @@ -7146,7 +7158,7 @@ Debug Message: - + R&ecord 錄製 (&E) @@ -7237,7 +7249,7 @@ Debug Message: - + None @@ -7263,722 +7275,737 @@ Debug Message: - + Small (32x32) - + Standard (64x64) - + Large (128x128) - + Full Size (256x256) - + Broken Vulkan Installation Detected - + Vulkan initialization failed during boot. - + Running a game TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping - + Loading Web Applet... - - + + Disable Web Applet - + Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet? (This can be re-enabled in the Debug settings.) - + The amount of shaders currently being built - + The current selected resolution scaling multiplier. - + Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch. - + How many frames per second the game is currently displaying. This will vary from game to game and scene to scene. - + Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms. - + Unmute - + Mute - + Reset Volume - + + Configure Effects... + + + + &Clear Recent Files - + &Continue - + Warning: Outdated Game Format - + You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.<br>For an explanation of the various Switch formats Eden supports, out our user handbook. This message will not be shown again. - - + + Error while loading ROM! - + The ROM format is not supported. - + An error occurred initializing the video core. - + This is usually caused by outdated GPU drivers. Please see the log for more details. See: <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md'>How to access log files</a>. - + Error while loading ROM! %1 %1 signifies a numeric error code. - + %1<br>Please redump your files or ask on Discord/Stoat for help. %1 signifies an error string. - + An unknown error occurred. Please see the log for more details. - - + + (64-bit) - - + + (32-bit) - - + + %1 %2 %1 is the title name. %2 indicates if the title is 64-bit or 32-bit - + Closing software... - + Save Data - + Mod Data - + Error Opening %1 Folder - - + + Folder does not exist! - + Remove Installed Game Contents? - + Remove Installed Game Update? - + Remove Installed Game DLC? - + Remove Entry - + Delete OpenGL Transferable Shader Cache? - + Delete Vulkan Transferable Shader Cache? - + Delete All Transferable Shader Caches? - + Remove Custom Game Configuration? - + Remove Cache Storage? - + Remove File - + Remove Play Time Data - + Reset play time? - - + + RomFS Extraction Failed! - + There was an error copying the RomFS files or the user cancelled the operation. - + Full - + Skeleton - + Select RomFS Dump Mode - + Please select the how you would like the RomFS dumped.<br>Full will copy all of the files into the new directory while <br>skeleton will only create the directory structure. - + There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation > Configure > System > Filesystem > Dump Root - + Extracting RomFS... - - + + Cancel - + RomFS Extraction Succeeded! - + The operation completed successfully. - + Error Opening %1 - + Select Directory - + Properties - + The game properties could not be loaded. - + Switch Executable (%1);;All Files (*.*) %1 is an identifier for the Switch executable file extensions. - + Load File - + Open Extracted ROM Directory - + Invalid Directory Selected - + The directory you have selected does not contain a 'main' file. - + Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci) - + Install Files - + %n file(s) remaining - + Installing file "%1"... + - Install Results - + To avoid possible conflicts, we discourage users from installing base games to the NAND. Please, only use this feature to install updates and DLC. - + %n file(s) were newly installed - + %n file(s) were overwritten - + %n file(s) failed to install - + System Application - + System Archive - + System Application Update - + Firmware Package (Type A) - + Firmware Package (Type B) - + Game - + Game Update - + Game DLC - + Delta Title - + Select NCA Install Type... - + Please select the type of title you would like to install this NCA as: (In most instances, the default 'Game' is fine.) - + Failed to Install - + The title type you selected for the NCA is invalid. - + File not found - + File "%1" not found - + OK - + Function Disabled - + Compatibility list reporting is currently disabled. Check back later! - + Error opening URL - + Unable to open the URL "%1". - + TAS Recording - + Overwrite file of player 1? - + Invalid config detected - + Handheld controller can't be used on docked mode. Pro controller will be selected. - - + + Amiibo - - + + The current amiibo has been removed - + Error - - + + The current game is not looking for amiibos - + Amiibo File (%1);; All Files (*.*) - + Load Amiibo - + Error loading Amiibo data - + The selected file is not a valid amiibo - + The selected file is already on use - + An unknown error occurred - + No firmware available - + Firmware Corrupted - + Unknown applet - + Applet doesn't map to a known value. - + Record not found - + Applet not found. Please reinstall firmware. - + Capture Screenshot - + PNG Image (*.png) - + TAS state: Running %1/%2 - + TAS state: Recording %1 - + TAS state: Idle %1/%2 - + TAS State: Invalid - + &Stop Running - + Stop R&ecording - + Building: %n shader(s) - + Scale: %1x %1 is the resolution scaling factor - + Speed: %1% / %2% - + Speed: %1% - + Game: %1 FPS - + Frame: %1 ms - + NO AA - + + NO FX + + + + + FX + + + + VOLUME: MUTE - + VOLUME: %1% Volume percentage (e.g. 50%) - + Derivation Components Missing - + Decryption keys are missing. Install them now? - + Wayland Detected! - + Wayland is known to have significant performance issues and mysterious bugs. It's recommended to use X11 instead. @@ -7986,74 +8013,74 @@ Would you like to force it for future launches? - + Use X11 - + Continue with Wayland - + Don't show again - + Restart Required - + Restart Eden to apply the X11 backend. - + Slow - + Turbo - + Unlocked - + Select RomFS Dump Target - + Please select which RomFS you would like to dump. - + Are you sure you want to close Eden? - - - + + + Eden - + Are you sure you want to stop the emulation? Any unsaved progress will be lost. - + The currently running application has requested Eden to not exit. Would you like to bypass this and exit anyway? diff --git a/docs/policies/AI.md b/docs/policies/AI.md deleted file mode 100644 index 554a805331..0000000000 --- a/docs/policies/AI.md +++ /dev/null @@ -1,116 +0,0 @@ -# AI Policy - -AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase. - -- [AI Policy](#ai-policy) - - [Low-quality code](#low-quality-code) - - [Licensing concerns](#licensing-concerns) - - [Vibe-coding](#vibe-coding) - - [Commit messages](#commit-messages) - - [Miscellaneous concerns](#miscellaneous-concerns) - - [Unacceptable Use Examples](#unacceptable-use-examples) - - [Addendum: Commit Messages](#addendum-commit-messages) - -## Low-quality code - -AI is notorious for producing low-quality code; be it: - -- nonfunctional, -- verbose/inefficient, -- breaking other parts of the codebase, -- or writing/architecting in a completely different style - -All code, AI or not, is held under a **strict standard of excellence**. AI/LLM-generated code will fail this test 10 times out of 10. - -## Licensing concerns - -This is an area of ongoing litigation, and as such, is still very iffy. For the time being, know that allowing AI to ingest the codebase may end up with its copyleft code regurgitated into incompatibly-licensed proprietary or permissive software. For you, this means to **not** feed code into LLMs. - -AI models may have also ingested AGPLv3 code, which is license-incompatible with our codebase. - -## Vibe-coding - -Just don't. If you're not going to put the effort in to understand every line of code you wrote, neither will we, and your patch or pull request will be ignored. - -## Commit messages - -AI-generated commit messages are absolutely terrible, and your pull request or patch will immediately be rejected if you choose to do this. They are, quite simply, actively detrimental to our understanding of your changes, and if you're not willing to summarize the intent behind your changes, then we're not going to bother reading the code you wrote. - -Write concise, simple, and descriptive commit messages that actually convey the proper intent behind what your change is trying to do. - -See the [Addendum](#addendum-commit-messages) for an instance of how bad AI models are at commit messages. - -## Miscellaneous concerns - -- While many environmental concerns about AI are typically blown out of proportion, it *is* a legitimate issue, and should be taken into account. -- Many people have significant concerns over the ethics of AI usage due to inhumane and predatory behavior by large AI companies, particularly Anthropic and OpenAI. This can technically be avoided through the usage of local LLMs. -- LLMs have a tendency to add unicode characters (such as the arrow → and the em-dash — symbols) to their output, which can make viewing code or documents harder on command-line editors and viewers. -- Dedicated coding models--namely Claude--also like to add a lot of comments to overexplain every individual line of code it produces. This actually makes it *harder* to understand the code! - -## Unacceptable Use Examples - -Here are a few examples of unacceptable use: - -- Solving problems - - Slapping a few files and a "please fix bug XYZ" into an LLM is a recipe for disaster that will pretty much never work. -- Fully AI-generated code, aka "vibecoding" -- Writing code based on pseudo-instructions - - If you don't know how to write code, don't. If you've figured out the root cause (preferably without feeding random files into an LLM) and actively know what's going on, provide information to other developers or friends of yours who have knowledge of the language and/or the codebase. - -## Addendum: Commit Messages - -The patchset for pull request [#3422](https://git.eden-emu.dev/eden-emu/eden/pulls/3422) was fed into several LLMs to generate a commit message. One LLM produced the following: - -```txt -profile_manager: Refactor user deletion to use index instead of UUID - -This commit improves the user profile deletion flow in the following ways: - -1. **UI layer (ConfigureProfileManager)**: - - Replaces UUID-based deletion with index-based deletion - - Passes the selected row index from the tree view to the confirmation dialog - - Emits deleteUser(int index) signal from ConfigureProfileManagerDeleteDialog - - Removes lambda capture and std::function callback in favor of direct signal/slot connection - -2. **Delete dialog**: - - Changes SetInfo() signature to accept index instead of accept callback - - Stores index internally and emits it via deleteUser signal on acceptance - -3. **Core ProfileManager**: - - Refactors RemoveUser(UUID) → RemoveProfileAtIndex(index) - - Removes duplicated stable_partition + save logic from RemoveUser() - - Centralizes profile removal and save-file writing in RemoveProfileAtIndex() - -4. **Minor follow-up**: - - Adds profile_manager.ResetUserSaveFile() call in PopulateUserList() to ensure consistent state when refreshing the user list - -These changes eliminate redundant code, reduce the risk of UUID → index mismatch bugs, and make the deletion path more straightforward and maintainable. - -Fixes: potential inconsistency when user order changes between UI and backend -``` - -This is: - -- Needlessly verbose (nobody cares about most of these details) -- Doesn't address the actual purpose of the PR (fixing a double-deletion bug in the profile manager) -- Has unicode arrows (this is bad for command-line editors) -- Uses corporate and word-salad language - -Another (code-oriented) LLM output the following: - -```txt -profile(manager/ui): switch to index-based deletion and unify removal logic - -- Make ConfigureProfileManagerDeleteDialog emit a deleteUser(int) signal instead of taking a callback. Store the target index in the dialog (m_index) and emit it on acceptance. -- Change ConfigureProfileManager to pass an index to SetInfo and to accept an index in DeleteUser. Wire the dialog signal to the new DeleteUser slot. -- Update headers: add the signal and m_index, add TODO to move dialog to a .ui file, and update slot/signature declarations. -- Add ProfileManager::RemoveProfileAtIndex(std::size_t) and refactor RemoveUser(UUID) to call RemoveProfileAtIndex to avoid duplicated removal logic. Ensure the removal path marks saves as needed and writes the user save file. -- Ensure the profile list updates immediately after deletes by calling profile_manager.ResetUserSaveFile() when populating the user list (qlaunch fix). -- Misc: update SPDX copyright year and fix build breakages caused by the API changes. - -This consolidates profile removal behavior, fixes potential race conditions in the profile dialog, and removes duplicated removal code. -``` - -This has all of the same problems as the other one. Needlessly verbose, doesn't address *what* it actually fixes ("consolidates profile removal behavior"... okay, why? What does it fix?), etc. It even has the bonus of totally hallucinating the addition of a method! - -On a more "philosophical" note, LLMs tend to be geared towards *corporate language*, as that's what they're trained on. This is why AI-generated commit messages feel like "word salad", and typically pad out the commit message to make it *look* like a lot of things were changed (trust me, it's like that in the corporate world). They typically also drift towards unneeded buzzwords and useless implementation details. diff --git a/docs/user/CommandLine.md b/docs/user/CommandLine.md index b8d71ac61f..a63a41a27c 100644 --- a/docs/user/CommandLine.md +++ b/docs/user/CommandLine.md @@ -1,32 +1,44 @@ # User Handbook - Command Line -There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. - -## eden +There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments. - `./eden `: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application. -- `-g `: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. -- `-f`: Use fullscreen. -- `-u `: Select the index of the user to load as. -- `-input-profile `: Specifies input profile name to use (for player #0 only). +- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` +- `--config/-c`: Specify alternate configuration file. +- `--fullscreen/-f`: Use fullscreen. +- `--help/-h`: Display help. +- `--game/-g `: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. +- `--multiplayer/-m`: Specify multiplayer options. +- `--program/-p`: Specify the program arguments to pass (optional). +- `--user/-u `: Specify the user index. +- `--version/-v`: Display version and quit. +- `--input-profile/-i `: Specifies input profile name to use (for player #0 only). +- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings. +- `--filter/-x`: Sets the debug log filter irrespective of settings. +- `--singlecore/-s`: Forces single-core regardless of settings. + +Only the Qt frontend supports the following arguments: + - `-qlaunch`: Launch QLaunch. - `-hlaunch`: Launch homebrew launcher `nx-hbloader`. - Requires a copy of Atmosphere to be extracted onto `sdmc`. - This is a shorthand for `/sdmc/atmosphere/hbl.nsp`. - `-setup`: Launch setup applet. -## eden-cli +`eden` (provided with `--room`), and `eden-room` supports the following options as well: -- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` -- `--config/-c`: Specify alternate configuration file. -- `--fullscreen/-f`: Set fullscreen. -- `--help/-h`: Display help. -- `--game/-g`: Specify the game to run. -- `--multiplayer/-m`: Specify multiplayer options. -- `--program/-p`: Specify the program arguments to pass (optional). -- `--user/-u`: Specify the user index. -- `--version/-v`: Display version and quit. -- `--input-profile/-i`: Specifies input profile name to use (for player #0 only). -- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings. -- `--filter/-x`: Sets the debug log filter irrespective of settings. -- `--singlecore/-s`: Forces single-core regardless of settings. +- `-n/--room-name`: The name of the room. +- `-d/--room-description`: The room description. +- `-s/--bind-address`: The bind address for the room. +- `-p/--port`: The port used for the room. +- `-m/--max-members`: The maximum number of players for this room. +- `-w/--password`: The password for the room. +- `-g/--preferred-game`: The preferred game for this room. +- `-i/--preferred-game-id`: The preferred game-id for this room. +- `-u/--username`: The username used for announce. +- `-t/--token`: The token used for announce. +- `-a/--web-api-url`: yuzu Web API url. +- `-b/--ban-list-file`: The file for storing the room ban list. +- `-l/--log-file`: The file for storing the room log. +- `-h/--help`: Display this help and exit. +- `-v/--version`: Output version information and exit. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a211b7f013..cb1deceb96 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -48,6 +48,11 @@ if (NOT TARGET stb::headers) add_library(stb::headers ALIAS stb) endif() +AddJsonPackage(NAME zbic DOWNLOAD_ONLY) +set(ZBIC_INCLUDE_DIR + "${zbic_SOURCE_DIR}/src" + PARENT_SCOPE) + # ItaniumDemangle (Windows only) if (WIN32 AND NOT TARGET LLVM::Demangle) add_library(demangle demangle/ItaniumDemangle.cpp) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 3ae6d73994..69dd6a8e4f 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -259,7 +259,7 @@ android { externalNativeBuild { cmake { - version = "3.31.6" + version = "3.31.5" path = file("${edenDir}/CMakeLists.txt") } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 382f56f464..9ea2a9ee17 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later package org.yuzu.yuzu_emu.adapters diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index f6ad6b0145..6c33fc7812 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -20,7 +20,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { EMULATE_BGR565("emulate_bgr565"), RESCALE_HACK("rescale_hack"), CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"), - RELOCATE_36BIT_ADDRESS_SPACE("relocate_36bit_address_space"), USE_DOCKED_MODE("use_docked_mode"), USE_AUTO_STUB("use_auto_stub"), RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"), @@ -28,13 +27,9 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), - RENDERER_BARRIER_FEEDBACK_LOOPS("barrier_feedback_loops"), ENABLE_BUFFER_HISTORY("enable_buffer_history"), USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"), - ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"), - USE_UNIFIED_MEMORY("use_unified_memory"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"), - STALL_ON_GPU_FENCE_WAIT("stall_on_gpu_fence_wait"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"), RENDERER_DEBUG("debug"), RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"), @@ -42,6 +37,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_SAMPLE_SHADING("sample_shading"), RENDERER_FRAME_GEN("frame_gen"), RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"), + GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"), PICTURE_IN_PICTURE("picture_in_picture"), USE_CUSTOM_RTC("custom_rtc_enabled"), BLACK_BACKGROUNDS("black_backgrounds"), @@ -97,7 +93,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { ENABLE_QUICK_SETTINGS("enable_quick_settings"); -// external fun isFrameSkippingEnabled(): Boolean + //external fun isFrameSkippingEnabled(): Boolean external fun isFrameInterpolationEnabled(): Boolean override fun getBoolean(needsGlobal: Boolean): Boolean = diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index e8087cef5b..ea31692fbf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt @@ -32,6 +32,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { RENDERER_DYNA_STATE("dyna_state"), DMA_ACCURACY("dma_accuracy"), GPU_FENCE_BEHAVIOR("gpu_fence_behavior"), + FRAME_PACING_MODE("frame_pacing_mode"), AUDIO_OUTPUT_ENGINE("output_engine"), MAX_ANISOTROPY("max_anisotropy"), THEME("theme"), @@ -50,6 +51,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { FAST_CPU_TIME("fast_cpu_time"), CPU_TICKS("cpu_ticks"), FAST_GPU_TIME("fast_gpu_time"), + GPU_UNSWIZZLE_TEXTURE_SIZE("gpu_unswizzle_texture_size"), + GPU_UNSWIZZLE_STREAM_SIZE("gpu_unswizzle_stream_size"), + GPU_UNSWIZZLE_CHUNK_SIZE("gpu_unswizzle_chunk_size"), BAT_TEMPERATURE_UNIT("bat_temperature_unit"), CABINET_APPLET("cabinet_applet_mode"), CONTROLLER_APPLET("controller_applet_mode"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/GpuUnswizzleSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/GpuUnswizzleSetting.kt new file mode 100644 index 0000000000..a9a40f6903 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/GpuUnswizzleSetting.kt @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.ArrayRes +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting +import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting +import org.yuzu.yuzu_emu.features.settings.model.IntSetting + +class GpuUnswizzleSetting( + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + @ArrayRes val textureSizeChoicesId: Int, + @ArrayRes val textureSizeValuesId: Int, + @ArrayRes val streamSizeChoicesId: Int, + @ArrayRes val streamSizeValuesId: Int, + @ArrayRes val chunkSizeChoicesId: Int, + @ArrayRes val chunkSizeValuesId: Int +) : SettingsItem( + object : AbstractSetting { + override val key: String = SettingsItem.GPU_UNSWIZZLE_COMBINED + override val defaultValue: Any = false + override val isSaveable = true + override val isRuntimeModifiable = true + override val isSwitchable = true + override val pairedSettingKey: String = "" + override var global: Boolean + get() { + return BooleanSetting.GPU_UNSWIZZLE_ENABLED.global && + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global && + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global && + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global + } + set(value) { + BooleanSetting.GPU_UNSWIZZLE_ENABLED.global = value + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global = value + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global = value + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global = value + } + override fun getValueAsString(needsGlobal: Boolean): String = "combined" + override fun reset() { + BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset() + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset() + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset() + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset() + } + }, + titleId, + titleString, + descriptionId, + descriptionString +) { + override val type = SettingsItem.TYPE_GPU_UNSWIZZLE + + // Check if GPU unswizzle is enabled via the dedicated boolean setting + fun isEnabled(needsGlobal: Boolean = false): Boolean = + BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal) + + fun setEnabled(value: Boolean) = + BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value) + + fun enable() = setEnabled(true) + + fun disable() = setEnabled(false) + + fun getTextureSize(needsGlobal: Boolean = false): Int = + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal) + + fun setTextureSize(value: Int) = + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value) + + fun getStreamSize(needsGlobal: Boolean = false): Int = + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal) + + fun setStreamSize(value: Int) = + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value) + + fun getChunkSize(needsGlobal: Boolean = false): Int = + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal) + + fun setChunkSize(value: Int) = + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value) + + fun reset() = setting.reset() +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index d4be5d0140..f968a0ae18 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -141,12 +141,14 @@ abstract class SettingsItem( const val TYPE_SPINBOX = 12 const val TYPE_LAUNCHABLE = 13 const val TYPE_PATH = 14 + const val TYPE_GPU_UNSWIZZLE = 15 const val TYPE_FX_TOOLBAR = 16 const val TYPE_FX_PRESET = 17 const val TYPE_FX_SHADER = 18 const val TYPE_FX_BUTTON = 19 const val FASTMEM_COMBINED = "fastmem_combined" + const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined" val emptySetting = object : AbstractSetting { override val key: String = "" @@ -785,6 +787,45 @@ abstract class SettingsItem( valuesId = R.array.gpuValues ) ) + put( + SingleChoiceSetting( + IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE, + titleId = R.string.gpu_unswizzle_texture_size, + descriptionId = R.string.gpu_unswizzle_texture_size_description, + choicesId = R.array.gpuTextureSizeSwizzleEntries, + valuesId = R.array.gpuTextureSizeSwizzleValues + ) + ) + put( + SingleChoiceSetting( + IntSetting.GPU_UNSWIZZLE_STREAM_SIZE, + titleId = R.string.gpu_unswizzle_stream_size, + descriptionId = R.string.gpu_unswizzle_stream_size_description, + choicesId = R.array.gpuSwizzleEntries, + valuesId = R.array.gpuSwizzleValues + ) + ) + put( + SingleChoiceSetting( + IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE, + titleId = R.string.gpu_unswizzle_chunk_size, + descriptionId = R.string.gpu_unswizzle_chunk_size_description, + choicesId = R.array.gpuSwizzleChunkEntries, + valuesId = R.array.gpuSwizzleChunkValues + ) + ) + put( + GpuUnswizzleSetting( + titleId = R.string.gpu_unswizzle_settings, + descriptionId = R.string.gpu_unswizzle_settings_description, + textureSizeChoicesId = R.array.gpuTextureSizeSwizzleEntries, + textureSizeValuesId = R.array.gpuTextureSizeSwizzleValues, + streamSizeChoicesId = R.array.gpuSwizzleEntries, + streamSizeValuesId = R.array.gpuSwizzleValues, + chunkSizeChoicesId = R.array.gpuSwizzleChunkEntries, + chunkSizeValuesId = R.array.gpuSwizzleChunkValues + ) + ) put( SingleChoiceSetting( IntSetting.FAST_CPU_TIME, @@ -846,13 +887,6 @@ abstract class SettingsItem( descriptionId = R.string.cpuopt_unsafe_host_mmu_description ) ) - put( - SwitchSetting( - BooleanSetting.RELOCATE_36BIT_ADDRESS_SPACE, - titleId = R.string.relocate_36bit_address_space, - descriptionId = R.string.relocate_36bit_address_space_description - ) - ) put( SwitchSetting( BooleanSetting.RENDERER_REACTIVE_FLUSHING, @@ -860,13 +894,6 @@ abstract class SettingsItem( descriptionId = R.string.renderer_reactive_flushing_description ) ) - put( - SwitchSetting( - BooleanSetting.RENDERER_BARRIER_FEEDBACK_LOOPS, - titleId = R.string.renderer_barrier_feedback_loops, - descriptionId = R.string.renderer_barrier_feedback_loops_description - ) - ) put( SwitchSetting( BooleanSetting.ENABLE_BUFFER_HISTORY, @@ -874,20 +901,6 @@ abstract class SettingsItem( descriptionId = R.string.enable_buffer_history_description ) ) - put( - SwitchSetting( - BooleanSetting.ENABLE_GPU_BUFFER_READBACK, - titleId = R.string.enable_gpu_buffer_readback, - descriptionId = R.string.enable_gpu_buffer_readback_description - ) - ) - put( - SwitchSetting( - BooleanSetting.USE_UNIFIED_MEMORY, - titleId = R.string.use_unified_memory, - descriptionId = R.string.use_unified_memory_description - ) - ) put( SwitchSetting( BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS, @@ -902,13 +915,6 @@ abstract class SettingsItem( descriptionId = R.string.sync_memory_operations_description ) ) - put( - SwitchSetting( - BooleanSetting.STALL_ON_GPU_FENCE_WAIT, - titleId = R.string.stall_on_gpu_fence_wait, - descriptionId = R.string.stall_on_gpu_fence_wait_description - ) - ) put( SwitchSetting( BooleanSetting.BUFFER_REORDER_DISABLE, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/GpuUnswizzleDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/GpuUnswizzleDialogFragment.kt new file mode 100644 index 0000000000..e14bc7639e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/GpuUnswizzleDialogFragment.kt @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding +import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting + +class GpuUnswizzleDialogFragment : DialogFragment() { + private var position = 0 + private val settingsViewModel: SettingsViewModel by activityViewModels() + private lateinit var binding: DialogGpuUnswizzleBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + position = requireArguments().getInt(POSITION) + + if (settingsViewModel.clickedItem == null) dismiss() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext())) + val item = settingsViewModel.clickedItem as GpuUnswizzleSetting + + // Setup texture size dropdown + val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId) + val textureSizeValues = resources.getIntArray(item.textureSizeValuesId) + val textureSizeAdapter = ArrayAdapter( + requireContext(), + android.R.layout.simple_dropdown_item_1line, + textureSizeEntries.toMutableList() + ) + binding.dropdownTextureSize.setAdapter(textureSizeAdapter) + + // Setup stream size dropdown + val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId) + val streamSizeValues = resources.getIntArray(item.streamSizeValuesId) + val streamSizeAdapter = ArrayAdapter( + requireContext(), + android.R.layout.simple_dropdown_item_1line, + streamSizeEntries.toMutableList() + ) + binding.dropdownStreamSize.setAdapter(streamSizeAdapter) + + // Setup chunk size dropdown + val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId) + val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId) + val chunkSizeAdapter = ArrayAdapter( + requireContext(), + android.R.layout.simple_dropdown_item_1line, + chunkSizeEntries.toMutableList() + ) + binding.dropdownChunkSize.setAdapter(chunkSizeAdapter) + + // Load current values + val isEnabled = item.isEnabled() + binding.switchEnable.isChecked = isEnabled + + if (isEnabled) { + val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) + if (textureSizeIndex >= 0) { + binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) + } + + val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) + if (streamSizeIndex >= 0) { + binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) + } + + val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) + if (chunkSizeIndex >= 0) { + binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) + } + } else { + // Set default/recommended values when disabling + binding.dropdownTextureSize.setText(textureSizeEntries[3], false) + binding.dropdownStreamSize.setText(streamSizeEntries[3], false) + binding.dropdownChunkSize.setText(chunkSizeEntries[3], false) + } + + // Clear adapter filters after setText to fix rotation bug + textureSizeAdapter.filter.filter(null) + streamSizeAdapter.filter.filter(null) + chunkSizeAdapter.filter.filter(null) + + // Enable/disable dropdowns based on switch state + updateDropdownsState(isEnabled) + binding.switchEnable.setOnCheckedChangeListener { _, checked -> + updateDropdownsState(checked) + } + + val dialog = MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setView(binding.root) + .create() + + // Setup button listeners + binding.btnDefault.setOnClickListener { + // Reset to defaults + item.reset() + // Refresh values with adapters reset + val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) + if (textureSizeIndex >= 0) { + binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) + } + val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) + if (streamSizeIndex >= 0) { + binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) + } + val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) + if (chunkSizeIndex >= 0) { + binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) + } + // Clear filters + textureSizeAdapter.filter.filter(null) + streamSizeAdapter.filter.filter(null) + chunkSizeAdapter.filter.filter(null) + + settingsViewModel.setAdapterItemChanged(position) + settingsViewModel.setShouldReloadSettingsList(true) + } + + binding.btnCancel.setOnClickListener { + dialog.dismiss() + } + + binding.btnOk.setOnClickListener { + if (binding.switchEnable.isChecked) { + item.enable() + // Save the selected values + val selectedTextureIndex = textureSizeEntries.indexOf( + binding.dropdownTextureSize.text.toString() + ) + if (selectedTextureIndex >= 0) { + item.setTextureSize(textureSizeValues[selectedTextureIndex]) + } + + val selectedStreamIndex = streamSizeEntries.indexOf( + binding.dropdownStreamSize.text.toString() + ) + if (selectedStreamIndex >= 0) { + item.setStreamSize(streamSizeValues[selectedStreamIndex]) + } + + val selectedChunkIndex = chunkSizeEntries.indexOf( + binding.dropdownChunkSize.text.toString() + ) + if (selectedChunkIndex >= 0) { + item.setChunkSize(chunkSizeValues[selectedChunkIndex]) + } + } else { + // Disable GPU unswizzle + item.disable() + } + + settingsViewModel.setAdapterItemChanged(position) + settingsViewModel.setShouldReloadSettingsList(true) + dialog.dismiss() + } + + // Ensure filters are cleared after dialog is shown + binding.root.post { + textureSizeAdapter.filter.filter(null) + streamSizeAdapter.filter.filter(null) + chunkSizeAdapter.filter.filter(null) + } + + return dialog + } + + private fun updateDropdownsState(enabled: Boolean) { + binding.layoutTextureSize.isEnabled = enabled + binding.dropdownTextureSize.isEnabled = enabled + binding.layoutStreamSize.isEnabled = enabled + binding.dropdownStreamSize.isEnabled = enabled + binding.layoutChunkSize.isEnabled = enabled + binding.dropdownChunkSize.isEnabled = enabled + } + + companion object { + const val TAG = "GpuUnswizzleDialogFragment" + const val POSITION = "Position" + + fun newInstance( + settingsViewModel: SettingsViewModel, + item: GpuUnswizzleSetting, + position: Int + ): GpuUnswizzleDialogFragment { + val dialog = GpuUnswizzleDialogFragment() + val args = Bundle() + args.putInt(POSITION, position) + dialog.arguments = args + settingsViewModel.clickedItem = item + return dialog + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index c84d0160f5..8d47db94e0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -106,6 +106,10 @@ class SettingsAdapter( PathViewHolder(ListItemSettingBinding.inflate(inflater), this) } + SettingsItem.TYPE_GPU_UNSWIZZLE -> { + GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + SettingsItem.TYPE_FX_TOOLBAR -> { FxToolbarViewHolder( ListItemSettingFxToolbarBinding.inflate(inflater, parent, false), @@ -507,6 +511,14 @@ class SettingsAdapter( settingsViewModel.setShouldShowPathResetDialog(true) } + fun onGpuUnswizzleClick(item: GpuUnswizzleSetting, position: Int) { + GpuUnswizzleDialogFragment.newInstance( + settingsViewModel, + item, + position + ).show(fragment.childFragmentManager, GpuUnswizzleDialogFragment.TAG) + } + private class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean { return oldItem.setting.key == newItem.setting.key diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 369ba4178f..824a54181e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -545,14 +545,10 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_NVDEC_EMULATION.key) add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key) - add(BooleanSetting.STALL_ON_GPU_FENCE_WAIT.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) - add(BooleanSetting.RENDERER_BARRIER_FEEDBACK_LOOPS.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) - add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key) - add(BooleanSetting.USE_UNIFIED_MEMORY.key) add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key) add(HeaderSetting(R.string.hacks)) @@ -563,6 +559,7 @@ class SettingsFragmentPresenter( add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(IntSetting.ANDROID_PIPELINE_WORKERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key) + add(SettingsItem.GPU_UNSWIZZLE_COMBINED) add(HeaderSetting(R.string.extensions)) @@ -1528,7 +1525,6 @@ class SettingsFragmentPresenter( add(HeaderSetting(R.string.cpu)) add(IntSetting.CPU_BACKEND.key) - add(BooleanSetting.RELOCATE_36BIT_ADDRESS_SPACE.key) add(IntSetting.CPU_ACCURACY.key) add(BooleanSetting.USE_AUTO_STUB.key) add(SettingsItem.FASTMEM_COMBINED) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/GpuUnswizzleViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/GpuUnswizzleViewHolder.kt new file mode 100644 index 0000000000..06701eb077 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/GpuUnswizzleViewHolder.kt @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui.viewholder + +import android.view.View +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible + +class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : + SettingViewHolder(binding.root, adapter) { + private lateinit var setting: GpuUnswizzleSetting + + override fun bind(item: SettingsItem) { + setting = item as GpuUnswizzleSetting + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(item.description.isNotEmpty()) + binding.textSettingDescription.text = item.description + + binding.textSettingValue.setVisible(true) + val resMgr = binding.root.context.resources + + if (setting.isEnabled()) { + // Show a summary of current settings + val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId) + val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId) + val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize()) + val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?" + + val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId) + val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId) + val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize()) + val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?" + + val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId) + val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId) + val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize()) + val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?" + + binding.textSettingValue.text = "$textureSizeLabel ⋅ $streamSizeLabel ⋅ $chunkSizeLabel" + } else { + binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled) + } + + binding.buttonClear.setVisible(setting.clearable) + binding.buttonClear.setOnClickListener { + adapter.onClearClick(setting, bindingAdapterPosition) + } + + setStyle(setting.isEditable, binding) + } + + override fun onClick(clicked: View) { + if (!setting.isEditable) { + return + } + + adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition) + } + + override fun onLongClick(clicked: View): Boolean { + if (setting.isEditable) { + return adapter.onLongClick(setting, bindingAdapterPosition) + } + return false + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt index 41f03c2488..fe53a99e3e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt @@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.utils.GpuDriverMetadata +import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.NativeConfig import java.io.File @@ -146,11 +147,11 @@ class DriverViewModel : ViewModel() { private fun wipeGameShaders(game: Game) { viewModelScope.launch { withContext(Dispatchers.IO) { - val externalFilesDir = YuzuApplication.appContext.getExternalFilesDir(null) + val userDirectory = DirectoryInitialization.userDirectory ?: return@withContext val shaderDir = File( - externalFilesDir.absolutePath + - "/shader/" + game.settingsName.lowercase() + userDirectory + + "/cache/shader/" + game.settingsName.lowercase() ) if (shaderDir.exists()) { shaderDir.deleteRecursively() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 6717894c93..f5f0cda0e9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -100,7 +100,7 @@ class Game( companion object { val extensions: Set = HashSet( - listOf("xci", "nsp", "nca", "nro") + listOf("xci", "nsp", "nca", "nro", "nsz", "xcz") ) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt index 24073e2a92..a186b91688 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameProperties.kt @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2023 yuzu Emulator Project diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index 411b764e36..bb2337a310 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -109,7 +109,7 @@ object GameHelper { // File extensions considered as external content, buuut should // be done better imo. - private val externalContentExtensions = setOf("nsp", "xci") + private val externalContentExtensions = setOf("nsp", "xci", "nsz", "xcz") private fun scanContentContainersRecursive( files: Array, diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index 614fd2569c..8dfea40693 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -64,8 +64,8 @@ void EmuWindow_Android::OnTouchReleased(int id) { EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id); } -void EmuWindow_Android::OnFrameDisplayed(u32 presented_frames) { - UpdateObservedFrameRate(presented_frames); +void EmuWindow_Android::OnFrameDisplayed() { + UpdateObservedFrameRate(); UpdateFrameRateHint(); if (!m_first_frame) { @@ -75,16 +75,15 @@ void EmuWindow_Android::OnFrameDisplayed(u32 presented_frames) { } } -void EmuWindow_Android::UpdateObservedFrameRate(u32 presented_frames) { - m_presented_frames = static_cast((std::max)(presented_frames, 1u)); +void EmuWindow_Android::UpdateObservedFrameRate() { const auto now = Clock::now(); if (m_last_frame_display_time.time_since_epoch().count() != 0) { const auto frame_time = std::chrono::duration(now - m_last_frame_display_time); const float seconds = frame_time.count(); if (seconds > 0.0f) { - const float instantaneous_rate = m_presented_frames / seconds; + const float instantaneous_rate = 1.0f / seconds; if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f && - instantaneous_rate <= 240.0f * m_presented_frames) { + instantaneous_rate <= 240.0f) { constexpr float SmoothingFactor = 0.15f; if (m_smoothed_present_rate <= 0.0f) { m_smoothed_present_rate = instantaneous_rate; @@ -125,9 +124,18 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const { return QuantizeFrameRateHint(verified_rate); } +float EmuWindow_Android::GetPresentedFrameMultiplier() { + if (!Settings::values.frame_gen.GetValue()) { + return 1.0f; + } + return static_cast(std::clamp(Settings::values.frame_gen_multiplier.GetValue(), 2, 4)); +} + float EmuWindow_Android::GetFrameRateHint() const { - const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f); - const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * m_presented_frames; + const float presented_multiplier = GetPresentedFrameMultiplier(); + const float observed_rate = + std::clamp(m_smoothed_present_rate * presented_multiplier, 0.0f, 240.0f); + const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * presented_multiplier; if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) { const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f); @@ -151,7 +159,7 @@ float EmuWindow_Android::GetFrameRateHint() const { return frame_time_verified_hint; } - const float nominal_rate = 60.0f * m_presented_frames; + const float nominal_rate = 60.0f * presented_multiplier; if (!Settings::values.use_speed_limit.GetValue()) { return QuantizeFrameRateHint(nominal_rate); } diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index 67883946c9..fca83b91fa 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -41,7 +41,7 @@ public: ~EmuWindow_Android() = default; void OnSurfaceChanged(ANativeWindow* surface); - void OnFrameDisplayed(u32 presented_frames) override; + void OnFrameDisplayed() override; void OnTouchPressed(int id, float x, float y); void OnTouchMoved(int id, float x, float y); @@ -58,9 +58,10 @@ private: using Clock = std::chrono::steady_clock; void UpdateFrameRateHint(); - void UpdateObservedFrameRate(u32 presented_frames); + void UpdateObservedFrameRate(); [[nodiscard]] float GetFrameRateHint() const; [[nodiscard]] float GetFrameTimeVerifiedHint() const; + [[nodiscard]] static float GetPresentedFrameMultiplier(); [[nodiscard]] static float QuantizeFrameRateHint(float frame_rate); float m_window_width{}; @@ -72,7 +73,6 @@ private: float m_last_frame_rate_hint = -1.0f; float m_pending_frame_rate_hint = -1.0f; float m_smoothed_present_rate = 0.0f; - float m_presented_frames = 1.0f; Clock::time_point m_last_frame_display_time{}; Clock::time_point m_pending_frame_rate_since{}; std::uint32_t m_pending_frame_rate_hint_votes = 0; diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp index 49d92bca75..9b6cd44e9f 100644 --- a/src/android/app/src/main/jni/game_metadata.cpp +++ b/src/android/app/src/main/jni/game_metadata.cpp @@ -76,7 +76,9 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj auto const file_type = loader->GetFileType(); if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) return false; - if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) && !Loader::IsBootableGameContainer(file, file_type)) + if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI || + file_type == Loader::FileType::NSZ || file_type == Loader::FileType::XCZ) && + !Loader::IsBootableGameContainer(file, file_type)) return false; u64 program_id = 0; return loader->ReadProgramId(program_id) == Loader::ResultStatus::Success; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index bd4d843473..d4f7689934 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -738,35 +738,6 @@ const char* fallback_cpu_detection() { return s_result.c_str(); } -#ifdef ARCHITECTURE_arm64 -struct SystemDriverInfo { - VkPhysicalDeviceProperties properties{}; - VkPhysicalDeviceDriverProperties driver{}; -}; - -SystemDriverInfo QuerySystemDriverInfo(JNIEnv* env, jstring j_hook_lib_dir) { - const std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir); - const Common::DynamicLibrary library{adrenotools_open_libvulkan( - RTLD_NOW, 0, nullptr, hook_lib_dir.c_str(), nullptr, nullptr, nullptr, nullptr)}; - Vulkan::vk::InstanceDispatch dld; - const Vulkan::vk::Instance instance = Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1); - const std::vector devices = instance.EnumeratePhysicalDevices(); - if (devices.empty()) { - throw Vulkan::vk::Exception(VK_ERROR_INITIALIZATION_FAILED); - } - SystemDriverInfo info{}; - info.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; - VkPhysicalDeviceProperties2 properties2{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &info.driver, - .properties = {}, - }; - Vulkan::vk::PhysicalDevice(devices[0], dld).GetProperties2(properties2); - info.properties = properties2.properties; - return info; -} -#endif - } // namespace extern "C" { @@ -817,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jstring jprogramId, jstring jupdatePath) { - u64 program_id = EmulationSession::GetProgramId(env, jprogramId); + const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId)); std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::shared_ptr nsp = std::make_shared( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( @@ -825,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en for (const auto& item : nsp->GetNCAs()) { for (const auto& nca_details : item.second) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) { - auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL; + const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId()); if (update_id == program_id) { return true; } @@ -886,18 +857,34 @@ jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDri jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo( JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { - std::string version_string{"1.1.0"}; - std::string driver_name{"generic"}; #ifdef ARCHITECTURE_arm64 - try { - const SystemDriverInfo info = QuerySystemDriverInfo(env, j_hook_lib_dir); - const u32 driver_version = info.properties.driverVersion; - version_string = - fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), - VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); - driver_name = Vulkan::vk::GetDriverName(info.driver); - } catch (...) { - } + const char* file_redirect_dir_{}; + int featureFlags{}; + std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir); + auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); + auto driver_library = std::make_shared(handle); + InputCommon::InputSubsystem input_subsystem; + auto window = + std::make_unique(ANativeWindow_fromSurface(env, j_surf), driver_library); + + Vulkan::vk::InstanceDispatch dld; + Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance( + *driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android); + + auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo()); + + auto device = Vulkan::CreateDevice(vk_instance, dld, *surface); + + auto driver_version = device.GetDriverVersion(); + auto version_string = + fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), + VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); + auto driver_name = device.GetDriverName(); +#else + auto driver_version = "1.0.0"; + auto version_string = "1.1.0"; //Assume lowest Vulkan level + auto driver_name = "generic"; #endif jobjectArray j_driver_info = env->NewObjectArray(2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string)); env->SetObjectArrayElement(j_driver_info, 1, Common::Android::ToJString(env, driver_name)); @@ -906,12 +893,32 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo( jstring Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getGpuModel(JNIEnv *env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { #ifdef ARCHITECTURE_arm64 - try { - return Common::Android::ToJString(env, QuerySystemDriverInfo(env, j_hook_lib_dir).properties.deviceName); - } catch (...) { - } -#endif + const char* file_redirect_dir_{}; + int featureFlags{}; + std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir); + auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), + nullptr, nullptr, file_redirect_dir_, nullptr); + auto driver_library = std::make_shared(handle); + InputCommon::InputSubsystem input_subsystem; + auto window = + std::make_unique(ANativeWindow_fromSurface(env, j_surf), driver_library); + + Vulkan::vk::InstanceDispatch dld; + Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance( + *driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android); + + auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo()); + + auto device = Vulkan::CreateDevice(vk_instance, dld, *surface); + + const std::string model_name{device.GetModelName()}; + + window.release(); + + return Common::Android::ToJString(env, model_name); +#else return Common::Android::ToJString(env, "no-info"); +#endif } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) { diff --git a/src/android/app/src/main/res/layout/dialog_gpu_unswizzle.xml b/src/android/app/src/main/res/layout/dialog_gpu_unswizzle.xml new file mode 100644 index 0000000000..8299551618 --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_gpu_unswizzle.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index cf2c968f99..3be04d9186 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -28,7 +28,7 @@ السلوك إخفاء الطبقة تلقائيًا إخفاء طبقة ذراع التحكم باللمس تلقائيًا بعد مرور الوقت المحدد من عدم النشاط. - تمكين إخفاء الطبقة تلقائيًا + تفعيل إخفاء الطبقة تلقائيًا إخفاء الطبقة عند إدخال ذراع التحكم إخفاء تلقائي لطبقة عناصر التحكم باللمس عند استخدام ذراع تحكم فعلية. تظهر الطبقة مرة أخرى عند فصل ذراع التحكم. عكس أزرار التأكيد/الرجوع في وحدة التحكم @@ -51,7 +51,7 @@ التخصيص الرؤية طبقة الأداء - تمكين طبقة إحصائيات الأداء + تفعيل طبقة إحصائيات الأداء ضبط المعلومات التي يتم عرضها في طبقة إحصائيات الأداء عرض معدل الإطارات عرض معدل الإطارات الحالي @@ -81,7 +81,7 @@ عرض طبقة معلومات الجهاز - تمكين طبقة الجهاز + تفعيل طبقة الجهاز طبقة الجهاز ضبط المعلومات التي يتم عرضها في طبقة الجهاز @@ -98,8 +98,8 @@ مزامنة سرعة النواة قم بمزامنة سرعة النواة مع النسبة المئوية للسرعة القصوى لتحسين الأداء دون تغيير السرعة الفعلية للعبة. - تمكين محاكاة MMU المضيف - يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية. + تفعيل محاكاة MMU المضيف + يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تفعيله إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية. مقابض تصحيح الأخطاء للإستخدام في التطوير فقط. 0 إلى 65535 @@ -254,7 +254,7 @@ إعداد بيانات المحاكي يلزم وجود مفاتيح حتى يعمل المحاكي، ويوصى باستخدام الفيرموير ويكون ضروريًا لاستخدام تطبيق القائمة الرئيسية. منح الأذونات - منح أذونات اختيارية لاستخدام ميزات معينة للمحاكي + منح أذونات اختيارية لاستخدام ميزات محددة في المحاكي العديد من الألعاب تتطلب الوصول إلى الفيرموير لتشغيل بشكل صحيح. https://yuzu-mirror.github.io/help/quickstart/#guide-introduction الإشعارات @@ -342,7 +342,7 @@ دقة مسار التدفق البصري، كجزء من الناتج. ويُعد خفض هذه القيمة أرخص طريقة لاستعادة الأداء. توليد الإطار غير متاح يفتقر برنامج تشغيل وحدة معالجة الرسومات هذا إلى نموذج ذاكرة Vulkan أو دعم الدقة النصفية (float16) التي تتطلبها برامج التظليل الخاصة بـ «Lossless Scaling». - اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتمكين إنشاء الإطارات لاحقًا + اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتفعيل إنشاء الإطارات لاحقًا تثبيت ملف Lossless.dll يتطلب إنشاء الإطارات أن تكون لديك نسخة قانونية خاصة بك من ملف Lossless.dll المأخوذ من برنامج Lossless Scaling اختر نسخة أخرى من ملف Lossless.dll @@ -350,7 +350,7 @@ مدعوم غير مدعوم (لا يتوفر نموذج ذاكرة Vulkan أو float16) Lossless Scaling - قم بتوفير نسختك الخاصة من ملف Lossless.dll لتمكين توليد الإطارات + قم بتوفير نسختك الخاصة من ملف Lossless.dll لتفعيل توليد الإطارات مثبت غير مثبت استبدل @@ -502,9 +502,9 @@ نسبة الحد الأقصى للسرعة يحدد النسبة المئوية للحد من سرعة المحاكاة. %100 هي السرعة الطبيعية. القيم الأعلى أو الأقل ستزيد أو تقلل من الحد الأقصى للسرعة. سرعة تيربو - عند تمكين وضع التيربو، سيتم تشغيل المحاكاة بهذه السرعة. + عند تفعيل وضع التيربو، سيتم تشغيل المحاكاة بهذه السرعة. سرعة بطيئة - عند تمكين الوضع البطيء، سيتم تشغيل المحاكاة بهذه السرعة. + عند تفعيل الوضع البطيء، سيتم تشغيل المحاكاة بهذه السرعة. خلفية وحدة المعالجة المركزية دقة وحدة المعالجة المركزية %1$s%2$s @@ -576,10 +576,8 @@ يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت. استخدم التنظيف التفاعلي يحسن دقة العرض في بعض الألعاب على حساب الأداء. - تمكين سجل التخزين المؤقت + تفعيل سجل التخزين المؤقت يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب. - تفعيل قراءة مخزن وحدة معالجة الرسومات - يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي. مخازن الرؤوس المُحسّنة يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون). @@ -597,6 +595,17 @@ يُمكّن هذا الخيار من التعامل مع عملية إعادة تحجيم الألعاب بطريقة تقليدية باستخدام مسار إعادة التحجيم السريع استخدم تظليل غير متزامن يقوم بتجميع التظليل بشكل غير متزامن. قد يقلل ذلك من التقطعات ولكنه قد يؤدي أيضًا إلى حدوث أخطاء. + إعدادات إلغاء ترتيب بيانات وحدة معالجة الرسومات + قم بضبط معلمات فكّ تشابك النسيج المستندة إلى وحدة معالجة الرسومات أو تعطيلها تمامًا. اضبط هذه الإعدادات لتحقيق التوازن بين الأداء وجودة تحميل النسيج. + تفعيل إلغاء ترتيب بيانات وحدة معالجة الرسومات + تعطيل + الحد الأقصى لحجم النسيج في وحدة معالجة الرسومات بعد إعادة ترتيب البيانات + يُحدد هذا الخيار الحد الأقصى لحجم (ميغابايت) معالجة الصور باستخدام وحدة معالجة الرسومات. مع أن وحدة معالجة الرسومات أسرع في معالجة الصور المتوسطة والكبيرة، إلا أن وحدة المعالجة المركزية قد تكون أكثر كفاءة في معالجة الصور الصغيرة جدًا. اضبط هذا الخيار لتحقيق التوازن الأمثل بين سرعة معالجة الرسومات واستهلاك وحدة المعالجة المركزية. + حجم تدفق إلغاء ترتيب بيانات وحدة معالجة الرسومات + يحدد هذا الخيار حد البيانات لكل إطار لمعالجة الصور الكبيرة. القيم الأعلى تُسرّع تحميل الصور على حساب زيادة زمن استجابة الإطارات؛ أما القيم الأقل فتُقلل من الحمل الزائد على وحدة معالجة الرسومات، ولكنها قد تتسبب في ظهور الصور بشكل مفاجئ. + حجم كتلة إلغاء ترتيب بيانات وحدة معالجة الرسومات + يُحدد هذا الخيار عدد شرائح العمق التي تتم معالجتها لكل دفعة من الصور ثلاثية الأبعاد (3D). زيادة هذا العدد تُحسّن كفاءة الإنتاجية على وحدات معالجة الرسومات القوية، ولكنها قد تُسبب تقطعًا أو انقطاعًا في عمل برنامج التشغيل على الأجهزة ذات المواصفات الأقل قوة. + افتراضي إضافات @@ -605,7 +614,7 @@ يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في ExtendedDynamicState (EDS). كلما زادت القيمة، قلّ عدد عمليات تجميع خط الأنابيب بناءً على الحالة الديناميكية التي يدعمها برنامج التشغيل. معطل حالة ديناميكية لإدخال الرأس - يتيح تمكين هذه الميزة معالجة أكثر مرونة لمدخلات الرؤوس، مما قد يقلل من وقت تجميع خط الأنابيب في vertex/buffer. + يتيح تفعيل هذه الميزة معالجة أكثر مرونة لمدخلات الرؤوس، مما قد يقلل من وقت تجميع خط الأنابيب في vertex/buffer. تظليل العينة يسمح هذا الخيار بتنفيذ مُظلل الأجزاء لكل عينة في جزء متعدد العينات بدلاً من تنفيذه مرة واحدة لكل جزء. يُحسّن هذا من جودة الرسومات على حساب بعض الأداء. @@ -814,9 +823,9 @@ النظام وضع الإرساء، المنطقة، اللغة الرسومات - مستوى الدقة، الدقة، ذاكرة التخزين المؤقتة للتظليل + مستوى دقة المحاكاة، الدقة، ذاكرة التخزين المؤقتة للتظليل الإعدادات السريعة - تمكين الإعدادات السريعة + تفعيل الإعدادات السريعة السماح بالوصول إلى قائمة الإعدادات السريعة عن طريق التمرير و زر القائمة الصوت محرك الإخراج، مستوى الصوت @@ -1037,10 +1046,25 @@ كسر السرعة + صغير جدًا (16 ميغابايت) + صغير (32 ميغابايت) + قياسي (128 ميغابايت) + كبير (256 ميغابايت) + كبير جدًا (512 ميغابايت) + منخفض جدًا (4 ميغابايت) + منخفض (8 ميغابايت) + قياسي (16 ميغابايت) + متوسط (32 ميغابايت) + عالي (64 ميغابايت) + منخفض جدًا (32) + منخفض (64) + قياسي (128) + متوسط (256) + عالي (512) مئوية @@ -1071,7 +1095,6 @@ فوري متوازن دقيق - صارم محافظ عدواني @@ -1198,8 +1221,8 @@ وضع الطيران يمرر وضع الطيران إلى نظام تشغيل Switch - تمكين التطبيق الصغير للطبقة - تمكين التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره. + تفعيل التطبيق الصغير للطبقة + تفعيل التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره. إدارة ملف التعريف diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 307f739870..8f6b691176 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -508,6 +508,7 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Kann in einigen Spielen zu Fehlern oder Abstürzen führen. Asynchrone Shader Kompiliert Shader asynchron. Dies kann Ruckler reduzieren, aber auch Grafikfehler verursachen. + Standard Erweiterungen @@ -917,10 +918,25 @@ Wirklich fortfahren? Übertaktung + Sehr klein (16 MB) + Klein (32 MB) + Normal (128 MB) + Groß (256 MB) + Sehr groß (512 MB) + Sehr niedrig (4 MB) + Niedrig (8 MB) + Normal (16 MB) + Mittel (32 MB) + Hoch (64 MB) + Sehr niedrig (32) + Niedrig (64) + Normal (128) + Mittel (256) + Hoch (512) Celsius @@ -951,7 +967,6 @@ Wirklich fortfahren? Direkt Ausgewogen Genau - Strikt Konservativ Aggressiv diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 11c7fe296f..05135b515e 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -289,6 +289,7 @@ Obtenedor de controladores de la GPU Gestor de controladores de la GPU Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión + Añadir efecto Abrir lista Cerrar lista Restablecer a predeterminado @@ -521,8 +522,6 @@ Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento. Activar el historial del búfer Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos. - Activar la lectura del buffer de la GPU - Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional. Búferes de vértices optimizados Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior). @@ -539,6 +538,17 @@ Permite el manejo de versiones anteriores para el paso de configuración de reescalado para juegos mediante el uso de una ruta de reescalado rápida. Usar sombreadores asíncronos Compila los sombreadores de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos. + Ajustes de desentrelazado de la GPU + Configura los parámetros de desentrelazado de texturas basadas ​​en la GPU o desactívelos por completo. Modifique estos ajustes para equilibrar el rendimiento y la calidad de las texturas cargadas. + Activar desentrelazado de la GPU + Desactivado + Tamaño máximo de textura de desentrelazado de la GPU + Establece el tamaño máximo (en MB) para el desentrelazado de texturas basada en GPU. Aunque la GPU es más rápida para texturas medianas y grandes, la CPU puede ser más eficiente para texturas muy pequeñas. Ajuste este valor para encontrar el equilibrio entre la aceleración de la GPU y la sobrecarga de la CPU. + Tamaño del flujo de desentrelazado de la GPU + Establece el límite de datos por fotograma para desentrelazar texturas grandes. Los valores altos aceleran la carga de texturas, a coste de una mayor latencia por fotograma; los valores bajos reducen la carga de la GPU, pero pueden causar parpadeos visibles en las texturas. + Tamaño del trozo de desentrelazado de la GPU + Determina la cantidad de cortes de profundidad procesados ​​en un solo envío de texturas 3D. Aumentar este valor puede mejorar el rendimiento en una GPU de gama alta, pero puede causar tirones y problemas en los tiempos de respuesta en hardware más modesto. + Por defecto Extensiones @@ -975,10 +985,25 @@ Overclock + Muy pequeño (16 MB) + Pequeño (32 MB) + Normal (128 MB) + Grande (256 MB) + Muy grande (512 MB) + Muy bajo (4 MB) + Bajo (8 MB) + Normal (16 MB) + Medio (32 MB) + Alto (64 MB) + Muy bajo (32) + Bajo (64) + Normal (128) + Medio (256) + Alto (512) Celsius @@ -1009,7 +1034,6 @@ Inmediato Equilibrado Preciso - Estricto Conservador Agresivo diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index a73d1ac9fb..7507051dd7 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -482,6 +482,8 @@ Emuler BGR565 Utiliser les shaders asynchrones Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques. + Désactivé + Par défaut Extensions @@ -882,10 +884,25 @@ 8 Go (Dangereux) + Très petit (16 Mo) + Petit (32 Mo) + Normal (128 Mo) + Large (256 Mo) + Très large (512 Mo) + Très faible (4 Mo) + Faible (8 Mo) + Normal (16 Mo) + Moyen (32 Mo) + Élevé (64 Mo) + Très faible (32) + Faible (64) + Normal (128) + Moyen (256) + Élevé (512) Celsius diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 6dd50bbf29..c291a9276b 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -318,6 +318,13 @@ Целевая частота кадров Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить. Использовать фиксированный множитель + Вкл. + Выкл. + Фикс. + Авто + Включите или выключите генерацию кадров и выберите множитель кадров. + Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически. + Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ. Целевой размер очереди кадров Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода. Минимальная задержка (Без буферизации) @@ -328,12 +335,14 @@ Разрешение оценки движения Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность. Генерация кадров недоступна + В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling. Опционально. Укажите свой Lossless.dll для включения генерации кадров позже. Установить Lossless.dll Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling Выбрать другой файл Lossless.dll Генерация кадров Поддерживается + Не поддерживается (нет модели памяти Vulkan или float16) Предоставьте свою копию Lossless.dll для включения генерации кадров Установлена Не установлена @@ -558,8 +567,6 @@ Повышение точности рендеринга в некоторых играх за счет снижения производительности. Включить историю буфера Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх. - Включить обратное чтение буфера ГПУ - Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку. Оптимизированные вершинные буферы Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже). @@ -577,6 +584,17 @@ Включает старый метод обработки этапа перенастройки масштабирования для игр за счёт использования быстрого алгоритма перемасштабирования. Использовать асинхронные шейдеры Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты. + Настройки распаковки текстур (Unswizzle) + Настройте параметры распаковки текстур на стороне ГПУ либо полностью отключите эту функцию. Изменение этих параметров позволяет найти баланс между производительностью и качеством загрузки текстур. + Включить распаковку текстур (Unswizzle) + Отключено + Макс. размер текстуры Unswizzle + Задает максимальный размер (в МБ) текстур для преобразования формата (unswizzle) на ГПУ. Хотя ГПУ быстрее работает со средними и большими текстурами, ЦП может быть эффективнее для очень маленьких. Настройте это значение, чтобы найти баланс между ускорением на ГПУ и нагрузкой на ЦП. + Размер потока Unswizzle + Задает лимит данных на кадр для преобразования крупных текстур (unswizzle). Высокие значения ускоряют загрузку текстур ценой увеличения задержки кадра; низкие значения снижают нагрузку на ГПУ, но могут вызывать заметную постепенную подгрузку текстур. + Размер блока Unswizzle + Задает количество слоёв глубины, обрабатываемых за одну пачку для 3D-текстур. Увеличение этого значения улучшает пропускную способность на мощных ГПУ, но может вызывать подтормаживания или таймауты драйвера на слабом железе. + По умолчанию Расширения @@ -1017,10 +1035,25 @@ Разгон + Очень малый (16 МБ) + Малый (32 МБ) + Обычный (128 МБ) + Большой (256 МБ) + Очень большой (512 МБ) + Очень низкий (4 МБ) + Низкий (8 МБ) + Обычный (16 МБ) + Средний (32 МБ) + Высокий (64 МБ) + Очень малый (32) + Малый (64) + Обычный (128) + Средний (256) + Большой (512) Цельсий @@ -1051,7 +1084,6 @@ Мгновенный Сбалансированный Точный - Строгий Консервативный Агрессивный diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 62c9c090e1..b49e320806 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -500,6 +500,17 @@ Вмикає застарілу обробку масштабування для ігор, використовуючи швидкий шлях масштабування Асинхронні шейдери Компілює шейдери асинхронно. Це може зменшити затримки, але також може спричинити графічні баги. + Налаштування розпакування за допомогою ГП + Налаштуйте розпакування текстур за допомогою ГП або повністю вимкнути його. Відкоригуйте ці налаштування, щоб урівноважити продуктивність і якість завантаження текстур. + Увімкнути розпакування за допомогою ГП + Вимкнено + Максимальний розмір текстур для розпакування ГП за допомогою ГП + Встановлює максимальний розмір (МБ) для розпакування текстур за допомогою ГП. ГП швидше справляється з текстурами середніх і великих розмірів, а ЦП ефективніший для дуже маленьких. Налаштуйте, щоб збалансувати ГП-прискоренням і навантаженням на ЦП. + Розмір потоку розпакування за допомогою ГП + Встановлює обмеження даних на кадр для розпакування великих текстур. Вищі значення пришвидшують завантаження текстур за рахунок більших кадрових затримок; менші значення зменшують перевантаження ГП але може спричинити помітні появи текстур. + Розмір блоків розпакування за допомогою ГП + Визначає кількість зрізів глибини, оброблених за партію 3D-текстур. Збільшення здатне покращити пропускну здатність на потужних ГП, але може призвести до затримок або затримок драйвера зі слабшим устаткуванням. + Стандартно Розширення @@ -916,10 +927,25 @@ 8 ГБ (Небезпечно) + Дуже малий (16 МБ) + Малий (32 МБ) + Нормальний (128 МБ) + Великий (256 МБ) + Дуже великий (512 МБ) + Дуже низький (4 МБ) + Низький (8 МБ) + Нормальний (16 МБ) + Середній (32 МБ) + Високий (64 МБ) + Дуже низький (32) + Низький (64) + Нормальний (128) + Середній (256) + Високий (512) Цельсій diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 568d1d384e..6ae0f102b2 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -1,6 +1,7 @@ + 本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。

在您开始之前, 请定位您设备存储上的 prod.keys ]]> 文件。

了解更多]]>
错误与注意事项 当发生错误时显示通知。 未授予通知权限! @@ -290,6 +291,25 @@ GPU驱动获取器 GPU 驱动管理器 安装替代的驱动程序以获得更好的性能和精度 + 后处理效果 + 在渲染完成后应用 ReShade FX 效果 + 配置此游戏的效果链 + 添加效果 + 移除 + 打开列表 + 关闭列表 + 移除效果 + 预设已激活。在开始游戏前可以在后处理效果中编辑它。 + 数值已从原始预设被更改。 + 预设 + 新建预设 + 把您加载的效果及当前数值保存为一个预设,以便今后选用。 + 请给预设起一个不含等号的名称。 + 无预设 + 删除预设 + 重置预设值 + 重置为默认值 + 找不到效果。请在此文件中放置 .fx 文件: 帧生成 配置针对此游戏的帧生成设定 设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。 @@ -304,6 +324,13 @@ 60 FPS 90 FPS 120 FPS + 开 + 关 + 固定 + 自动 + 打开或关闭帧生成并选择帧生成倍数。 + 生成帧到目标帧率。倍率会自动调整。 + 用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。 帧队列目标 在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。 最低延迟 (无缓冲) @@ -314,12 +341,14 @@ 运动预估分辨率 光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。 帧生成不可用 + 这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。 作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。 安装 Lossless.dll 帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本 选择其它 Lossless.dll 副本 帧生成 支持 + 不支持 (无 Vulkan 内存模型或 float16) 无损缩放 提供您自己的 Lossless.dll 文件以启用帧生成 已安装 @@ -539,8 +568,6 @@ 通过牺牲性能来提升某些游戏的渲染精度。 启用缓冲区历史 启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。 - 启用 GPU 缓冲区回读 - 在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。 优化顶点缓冲区 启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。 @@ -558,6 +585,17 @@ 启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式 使用异步着色器 以异步方式编译着色器。采用此方式或可减少卡顿,但也可能引入故障点。 + GPU Unswizzle 设置 + 配置基于 GPU 的纹理 unswizzling 参数,或完全禁用该功能。通过调整这些设置,以尝试在性能与纹理加载质量之间取得平衡。 + 启用 GPU Unswizzle + 禁用 + GPU Unswizzle 最大纹理尺寸 + 设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中等和大型纹理的速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以平衡GPU 加速与 CPU 开销。 + GPU Unswizzle 流大小 + 设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加速纹理的加载过程,但会带来更高的帧延迟。而较低的数值则可以降低 GPU 的开销,但也可能会导致可见的纹理闪现。 + GPU Unswizzle 块大小 + 定义了 3D 纹理每批次处理的深度切片数量。增加此数值可在高性能 GPU 上提升吞吐效率,但在性能较弱的硬件上可能会导致卡顿或驱动超时。 + 默认 扩展 @@ -598,6 +636,11 @@ 日志记录 按行刷新调试日志 在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。 + 开启扩展日志 + 将最大日志文件大小从 100 MiB 提升至 1GiB。 + 日志筛选器 + 控制 Eden 的日志类别。例如: *:Info Service.LM:Debug + GPU 日志 日志等级 @@ -881,6 +924,8 @@ ROM 文件不存在 游戏需要固件 + + 正在搜索游戏... 未找到标题ID的游戏: %1$s @@ -991,10 +1036,25 @@ 超频 + 极小 (16 MB) + 较小 (32 MB) + 正常 (128 MB) + 较大 (256 MB) + 极大 (512 MB) + 极低 (4 MB) + 低 (8 MB) + 正常 (16 MB) + 中 (32 MB) + 高 (64 MB) + 极低 (32) + 低 (64) + 正常 (128) + 中 (256) + 高 (512) 摄氏度 @@ -1025,7 +1085,6 @@ 即时 均衡 精确 - 严格 保守式 主动式 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 15a3b9645d..1391f1bc6e 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -1,12 +1,12 @@ - 本軟體可執行Nintendo Switch主機的遊戲,軟體不提供遊戲和金鑰檔案。

在開始之前,請先安裝您的 prod.keys ]]> 檔案,了解更多]]>
+ 本軟體可執行 Nintendo Switch 主機的遊戲,軟體不提供遊戲和金鑰檔案。

在開始之前,請先安裝您的 prod.keys ]]> 檔案,了解更多]]>
通知和錯誤 發生錯誤時顯示通知 未授予通知權限! - Eden模擬器通知 - Eden正在執行 + Eden 模擬器通知 + Eden 正在執行 秒 @@ -16,7 +16,7 @@ 範圍最大必須為%1$d 無效的範圍 - 使用個別設定中 + 使用個別遊戲設定中 顯示虛擬按鍵 @@ -32,7 +32,7 @@ 使用控制器時自動隱藏虛擬按鍵 在使用控制器時自動隱藏畫面上的虛擬按鍵,會在控制器斷開連接後重新出現 反轉手把的 確認/返回 按鈕 - 使用Eden時將手把的確認及返回鍵反轉以符合Switch與Xbox的操作方式 + 使用 Eden 時將手把的確認及返回鍵反轉以符合 Switch 與 Xbox 的操作方式 虛擬按鍵設定 調整虛擬按鍵的位置與顯示設定 @@ -54,7 +54,7 @@ 啟用效能統計疊加層 設定疊加層中顯示的資訊 顯示影格率 - 顯示當前影格率 + 顯示目前的影格率 顯示影格時間 顯示應用程式的記憶體用量 顯示模擬器的記憶體用量 @@ -85,11 +85,11 @@ 裝置疊加層 設定裝置疊加層中顯示的資訊 - 顯示Eden的組建版本 + 顯示 Eden 的組建版本 顯示圖形驅動程式的版本 顯示裝置型號 - 顯示GPU型號 - 顯示CPU型號 + 顯示 GPU 型號 + 顯示 CPU 型號 顯示韌體版本 @@ -99,13 +99,13 @@ 同步核心速度 將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能 啟用主機 MMU 模擬 - 此最佳化可加速客體程式的記憶體存取。啟用後,客體記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。 + 此最佳化可加速客體程式的記憶體存取。啟用後,客體記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬 偵錯開關 僅限開發用途 - 0到65535 + 0 到 65535 - NVDEC模擬 + NVDEC 模擬 若在過場動畫中當機請切換成 CPU 無 @@ -121,7 +121,7 @@ 加入 瀏覽公開房間 使用者名稱 - IP位址 + IP 位址 埠 房間建立成功 成功加入房間 @@ -129,8 +129,8 @@ 網路錯誤 連線中斷 使用者名稱已被其他人使用 - MAC位址衝突 - 主機ID衝突 + MAC 位址衝突 + 主機 ID 衝突 版本不符 密碼錯誤 無法連接 @@ -143,7 +143,7 @@ 您已被房主踢出 未知錯誤 房間未初始化 - 房間閒置 + 房間閒置中 正在加入房間 已加入房間 房間管理員 @@ -151,7 +151,7 @@ %1$s 離開 %1$s 被踢出 %1$s 被封鎖 - 位址已解封 + IP 位址已解封 踢出成員 密碼 正在加入… @@ -173,7 +173,7 @@ 封鎖使用者 公開房間 未找到公開房間 - 需要輸入密碼 + 請輸入密碼 :%1$d/%2$d 遊戲 任意遊戲 @@ -184,14 +184,14 @@ 搜尋房間… 遊戲 大廳類型 - 長度需為3-20個字元 - 必填 - 需要Web令牌,請前往進階設定 -> 系統 -> 網路 - IP格式無效 - 必須介於4-20個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式) - 使用者名稱無效,請在系統→網路中檢查設定 - 必須為48個字元,且僅包含小寫字母a-z - 埠號需為1-65535 + 長度需為 3-20 個字元 + 請填寫這個欄位 + 需要 Web 令牌,請前往進階設定 -> 系統 -> 網路 + IP 格式無效 + 必須介於 4-20 個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式) + 使用者名稱無效,請在系統 -> 網路中檢查設定 + 必須為 48 個字元,且僅包含小寫字母 a-z + 埠號需為 1-65535 取消 確定 重新整理 @@ -201,16 +201,16 @@ 歡迎! - 瞭解如何設定 Eden 並開始模擬。 + 瞭解如何設定 Eden 並開始模擬 開始使用 金鑰 - 使用下方的按鈕安裝您的 prod.keys 檔案。 + 使用下方的按鈕安裝您的 prod.keys 檔案 韌體 使用下方的按鈕安裝您的firmware.zip檔案 遊戲資料夾 使用下方的按鈕選擇您的遊戲資料夾。 完成 - 您已準備就緒。\n盡情遊玩您的遊戲! + 您已準備就緒\n盡情遊玩您的遊戲! 下一步 上一步 新增遊戲 @@ -228,8 +228,8 @@ 不再顯示 遊戲目錄新增成功 檢查更新 - 在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新 - 偵測到新版本 + 在 Eden 啟動時檢查有無新版本並選擇是否要下載與安裝更新 + 發現新版本 有更新可供安裝:%1$s\n\n您要進行更新嗎? 下載更新中 更新下載失敗 @@ -237,7 +237,7 @@ 更新安裝失敗:%1$s 搜尋 設定 - 找不到檔案,或者尚未選取遊戲目錄 + 找不到遊戲檔案,或者尚未選取遊戲目錄 管理遊戲資料夾 允許 Eden 尋找您的遊戲檔案 跳過選擇遊戲資料夾? @@ -252,7 +252,7 @@ https://yuzu-mirror.github.io/help/quickstart/#guide-introduction 跳過安裝韌體? 設定模擬器資料 - 模擬器需要金鑰才能正常執行,同時建議安裝韌體以啟動QLaunch小程式 + 模擬器需要金鑰才能正常執行,同時建議安裝韌體以啟動 QLaunch 小程式 授予權限 授予權限以使用模擬器的特定功能 許多遊戲需要韌體才能正常運作 @@ -273,10 +273,10 @@ 版本號碼 基本資訊 硬體 - 支援的ABI - CPU資訊 - GPU資訊 - Vulkan驅動程式版本 + 支援的 ABI + CPU 資訊 + GPU 資訊 + Vulkan 驅動程式版本 取得裝置資訊失敗 記憶體資訊 記憶體大小 @@ -287,8 +287,8 @@ 跳過 取消 安裝 Amiibo 金鑰 - 若要在遊戲中使用 Amiibo 得先安裝 - GPU驅動程式下載器 + 需要先安裝才能在遊戲中使用 Amiibo + GPU 驅動程式下載器 GPU 驅動程式管理員 安裝替代驅動程式以取得潛在的更佳效能或準確度 後製處理效果 @@ -299,7 +299,7 @@ 開啟清單 收起清單 移除效果 - 濾鏡已啟用。請在開啟遊戲前於 後製處理效果 中調整 + 濾鏡已啟用。請在開啟遊戲前於\"後製處理效果\"中調整 數值已變更,與原始濾鏡設定不同 濾鏡 新增濾鏡 @@ -312,42 +312,51 @@ 找不到設定檔。請將濾鏡效果的 .fx 檔案放置於此資料夾: 影格生成 調整此遊戲的影格生成設定 - 使用 Lossless Scaling 在已渲染的影格之間插入補間影格。啟用此功能時,會強制採用 FIFO 垂直同步 + 使用Lossless Scaling在已渲染的影格之間插入補間影格。啟用此功能時,會強制採用FIFO垂直同步 影格倍率 - 設定在每個已渲染的影格中要顯示的影格數。數值越高所需的 GPU 運算時間也會按比例增加。若要求的影格數超過裝置顯示器能呈現的數量將會降低模擬速度 + 設定在每個已渲染的影格中要顯示的影格數,數值越高所需的GPU運算時間也會按比例增加。若要求的影格數超過裝置顯示器能呈現的數量將會降低模擬速度 2x 3x 4x 目標影格率 - 選擇裝置顯示器能實際呈現的影格率。之後倍率器會自動升高或降低以維持影格率。如果某個調整導致遊戲本身運作變慢,則會自動復原該設定 + 選擇裝置顯示器能實際呈現的影格率,之後倍率器會自動升高或降低以維持影格率。如果某個調整導致遊戲本身運作變慢,則會自動復原該設定 使用固定倍率 60 FPS 90 FPS 120 FPS + 開啟 + 關閉 + 固定 + 自動 + 開啟或關閉影格生成後選擇影格倍率 + 選擇要生成的目標影格率,倍率器會自動調整 + 用來預測影格之間動態的解析度,較低的數值可以減少 GPU 運算時間 影格佇列目標 最多允許多少個已完成的影格在顯示器前等待,較大的佇列可以吸收 GPU 突發負載,但會增加輸入延遲 最低延遲(無緩衝) - 平衡(1影格) - 最流暢(2影格) + 平衡(1 影格) + 最流暢(2 影格) 配合遊戲調整運動預測 - 以遊戲實際渲染的解析度而非升頻後的輸出進行運動預測。由於升頻後不會增加任何動態細節,因此不會影響準確度 + 以遊戲實際渲染的解析度而非升頻後的輸出進行運動預測,由於升頻後不會增加任何動態細節,因此不會影響準確度 運動預測解析度 - 光流處理的解析度,以輸出解析度的比例表示。降低此設定值是減少效能負載最有效的方法 + 光流處理的解析度,以輸出解析度的比例表示,降低此設定值是減少效能負載最有效的方法 無法使用影格生成 + 此 GPU 驅動程式缺少 Lossless Scaling 著色器所需的 Vulkan 記憶體模型或半精度浮點數(float16)支援 可選擇安裝自己擁有的 Lossless.dll 以在之後啟用影格生成功能 安裝 Lossless.dll 您需要擁有從合法管道獲得的 Lossless Scaling 並從該軟體的資料夾中取得 Lossless.dll 檔案才能使用影格生成功能 安裝不同的 Lossless.dll 檔案 影格生成 支援 + 不支援(無 Vulkan 記憶體模型或 float16) Lossless Scaling 安裝您擁有的 Lossless.dll 檔案以使用影格生成 已安裝 未安裝 - 替換 + 更換 刪除 刪除已安裝的 Lossless.dll 及預先編譯的著色器 - 影格生成功能會停止運作直到您重新安裝 Lossless.dll 。您的 Lossless.dll 原始檔並不會被 Eden 修改 + 影格生成功能會停止運作直到您重新安裝Lossless.dll,您的 Lossless.dll原始檔不會被 Eden 修改 Lossless.dll 未安裝 前往 設定 -> Lossless Scaling 安裝以使用影格生成功能 請先關閉遊戲 @@ -360,33 +369,33 @@ 無法複製選取的檔案 無法讀取選取的檔案 選取的檔案並不是 Windows 程式庫。請在 Lossless Scaling 的安裝位置(資料夾)中選擇 Lossless.dll - 這個 Lossless.dll 的副本並未包含影格生成著色器。請更新 Lossless Scaling 後再試一次 - 無法轉譯影格生成著色器。尚不支援此 Lossless Scaling 版本 - 轉譯的著色器無法寫入到儲存空間。請確認手機容量是否充足 + 這個 Lossless.dll 的副本並未包含影格生成著色器。請更新 Lossless Scaling 後重試 + 無法轉譯影格生成著色器,尚不支援此 Lossless Scaling 版本 + 轉譯的著色器無法寫入到儲存空間,請確認手機容量是否充足 進階設定 調整模擬器設定 最近遊玩 最近新增 開啟 Eden 資料夾 管理 Eden 的內部檔案 - 自訂Eden的主題與介面 + 自訂 Eden 的主題與介面 找不到檔案管理員 無法開啟 Eden 目錄 請使用檔案管理員的側邊面板手動定位到使用者資料夾 管理儲存資料 - 導入/導出遊戲的儲存資料\n壓縮檔必須為.zip格式\n壓縮檔中首個子資料夾名稱必須為遊戲 ID - 導入儲存資料 - 這將會以提供的檔案覆寫現有的該遊戲儲存資料,您確定要繼續嗎? - 正在導入儲存資料… - 正在導出儲存資料… - 已成功導入 + 匯入/匯出遊戲的儲存資料\n壓縮檔必須為.zip格式\n壓縮檔中首個子資料夾名稱必須為遊戲ID + 匯入儲存資料 + 這將會以提供的檔案覆蓋現有的該遊戲儲存資料,您確定要繼續嗎? + 正在匯入儲存資料… + 正在匯出儲存資料… + 已成功匯入 無效的儲存目錄結構 首個子資料夾名稱必須為遊戲 ID 安裝韌體 韌體必須為 ZIP 壓縮檔,將會用於部分遊戲的啟動 正在安裝韌體 韌體安裝失敗 - 請確保韌體 nca 檔案位於 zip 壓縮檔的根目錄,然後再試一次。 + 請確保韌體 nca 檔案位於 zip 壓縮檔的根目錄後再試一次 韌體解除安裝失敗 分享偵錯記錄 分享 Eden 的記錄檔以便對相關問題進行偵錯 @@ -406,7 +415,7 @@ %1$d 覆寫成功 https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates 您的裝置不支援自訂驅動程式 - 此裝置不支援注入自訂驅動程式。\n請以後再來查看是否已新增支援! + 此裝置不支援注入自訂驅動程式\n請以後再來查看是否已新增支援! 管理 Eden 資料 安裝韌體、金鑰,導入/導出使用者資料及安裝其它項目! 遊戲資料夾 @@ -437,12 +446,12 @@ 金鑰安裝失敗 金鑰安裝成功 一個或多個金鑰安裝失敗 - 請確保金鑰檔案具有.keys副檔名後重試。 - 金鑰初始化失敗。請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰。 + 請確保金鑰檔案具有 .keys 副檔名後重試 + 金鑰初始化失敗,請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰 Qlaunch - 從Switch系統主畫面啟動應用程式(目前僅支援英文) + 從 Switch 系統主畫面啟動應用程式(目前僅支援英文) 小程式啟動器 使用已安裝的韌體啟動系統小程式 未安裝韌體 @@ -469,19 +478,19 @@ 這些專案使 Eden Android 版成為可能 組建版本 使用者資料 - 導入/導出所有應用程式資料。\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入使用者資料可能會出現問題,建議手動導入所有所需資料。 - 正在導出使用者資料… - 正在導入使用者資料… + 匯入/匯出所有應用程式資料\n\n匯入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 匯入使用者資料可能會出現問題,建議手動匯入所有所需資料 + 正在匯出使用者資料… + 正在匯入使用者資料… 無效的 Eden 備份 - 使用者資料導出成功 - 使用者資料導入成功 - 導出已取消 - 請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案,並再試一次 + 使用者資料匯出成功 + 使用者資料匯入成功 + 匯出已取消 + 請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案後重試 限制模擬速度 將模擬速度限制在標準速度的指定百分比 限制速度百分比 - 指定模擬速度的百分比。100% 為標準速度,更高或更低的值將會減少或增加速度限制 + 指定模擬速度的百分比,100% 為標準速度,更高或更低的值將會減少或增加速度限制 加速模式 當開啟加速模式時,模擬器將以此速度執行 慢速模式 @@ -508,19 +517,19 @@ CPU 時脈 提高模擬 CPU 回報的時脈,可以減少某些 FPS 限制。較弱的 CPU 可能會出現效能下降的情況,部分遊戲可能會出現異常 - 自訂CPU時脈 - 自訂CPU時脈。更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為77-21000。 + 自訂 CPU 時脈 + 自訂 CPU 時脈,更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為 77-21000 時脈 - 記憶體佈局 - (實驗性)更改模擬記憶體佈局。此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在 RAM 為 8GB 或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的CPU後端改為 Dynarmic (JIT) + 記憶體布局 + (實驗性)更改模擬記憶體布局,此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在 RAM 為 8GB 或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的 CPU 後端改為 Dynarmic(JIT) 生成 網路令牌 - 用於建立公開大廳的網路令牌。由48個小寫字母a-z組成 + 用於建立公開大廳的網路令牌,由 48 個小寫字母 a-z 組成 網路使用者名稱 - 多人遊戲房間中顯示的使用者名稱。必須為4-20個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式) + 多人遊戲房間中顯示的使用者名稱。必須為 4-20 個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式) 網路 @@ -535,55 +544,64 @@ 進階 GPU 模式 - 設定 GPU 模擬的準確度。大多數遊戲在設定為快速時即可正常渲染,但有些需要渲染粒子的遊戲仍需設為準確來避免圖形錯誤 + 設定 GPU 模擬的準確度,大多數遊戲在設定為快速時即可正常渲染,但有些需要渲染粒子的遊戲仍需設為準確來避免圖形錯誤 DMA 準確度 - 控制 DMA 準確度。將準確度設為穩定可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為預設 + 控制 DMA 準確度,將準確度設為穩定可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為預設 GPU 同步柵欄模式 - 控制 GPU 同步柵欄模式。即時是速度最快的選項,但可能會導致一些問題。使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤。準確則是在犧牲部分效能的情況下進一步提升相容性。嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題。預設則會依照 GPU 準確度設定 + 控制 GPU 同步柵欄模式,即時是速度最快的選項,但可能會導致一些問題;使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤;準確則是在犧牲部分效能的情況下進一步提升相容性;嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題;預設則會依照 GPU 準確度設定 各向異性過濾 改善斜角檢視時的紋理品質 - VRAM使用模式 - 控制GPU記憶體的分配與釋放策略 - ASTC解碼方式 - 選擇ASTC壓縮紋理的解碼方式:CPU(慢速、穩定)、GPU(快速、推薦)或CPU非同步(無卡頓,可能導致問題) + VRAM 使用模式 + 控制 GPU 記憶體的分配與釋放策略 + ASTC 解碼方式 + 選擇 ASTC 壓縮紋理的解碼方式:CPU(慢速、穩定)、GPU(快速、推薦)或 CPU 非同步(無卡頓,可能導致問題) 同步記憶體操作 - 確保計算和記憶體操作之間的資料一致性。 此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。 使用Unreal Engine 4的遊戲似乎受影響最大 + 確保計算和記憶體操作之間的資料一致性,此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。使用 Unreal Engine 4 的遊戲似乎受影響最大 磁碟著色器快取 將產生的著色器快取儲存至硬碟以減少中斷 - 強制使用最大時脈 (僅限Adreno) - 強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用) + 強制使用最大時脈(僅限 Adreno) + 強制GPU以可能的最大時脈運作(熱溫限制仍會被套用) GPU 非同步模擬 此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率 使用重新啟用排清 - 犧牲效能,以改善部分遊戲的轉譯準確度 + 犧牲效能以改善部分遊戲的轉譯準確度 啟用緩衝區歷史 - 允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性 - 啟用 GPU 緩衝區讀回 - 透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤 + 允許存取先前的緩衝區狀態,此選項可能會改善部分遊戲的渲染品質與效能穩定性 最佳化頂點緩衝區 - 啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本) + 啟用最佳化的頂點緩衝區綁定,需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機(25.3 版或更低的版本) 修改 GPU 時脈 讓遊戲誤以為 GPU 工作能更快完成,避免遊戲為了配合 Switch 的時脈而降低解析度與渲染距離 - 跳過CPU內部失效處理 - 在記憶體更新期間跳過某些CPU端快取的失效處理,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或當機 + 跳過 CPU 內部失效處理 + 在記憶體更新期間跳過某些 CPU 端快取的失效處理,減少 CPU 使用率並提高其性能。可能會導致某些遊戲出現故障或當機 修正光暈特效 - 在 薩爾達傳說:織夢島 和 薩爾達傳說:智慧的再現 中 減少光暈模糊的問題並移除 橫衝直撞:狂飆樂園重製版 中的光暈效果 (Adreno A6XX - A7XX/ Turnip)。警告:可能會在其它遊戲中導致圖形異常 + 在 \"薩爾達傳說:織夢島\" 和 \"薩爾達傳說:智慧的再現\" 中減少光暈模糊的問題並移除 \"橫衝直撞:狂飆樂園重製版\" 中的光暈效果(適用於 Adreno A6XX - A7XX/ Turnip)。警告:可能會在其它遊戲中導致圖形異常 模擬 BGR565 - 修正遊戲中出現的顏色反轉、圖形異常或陰影異常 + 修正遊戲中出現的色彩反轉、圖形異常或陰影異常 啟用舊版重新縮放處理 透過快速重新縮放處理來啟用遊戲重新縮放設定階段的舊版處理方式 使用非同步著色器 - 使用非同步編譯著色器。這可能會減少卡頓,但也可能導致圖形錯誤。 + 使用非同步編譯著色器,這可能會減少卡頓,但也可能導致圖形錯誤 + GPU Unswizzle 設定 + 設定基於 GPU 的 Unswizzling 參數或完全停用此功能,調整此設定以在性能與紋理載入品質中取得平衡 + 啟用 GPU Unswizzle + 停用 + GPU Unswizzle 最大紋理尺寸 + 設定 GPU 紋理 Unswizzling 的最大值(MB),雖然 GPU 處理中型和大型紋理的速度較快,但 CPU 處理非常小的紋理可能更有效率。調整此設定以在 GPU 加速與 CPU 負載之間取得平衡 + GPU Unswizzle Stream 大小 + 設定每個影格處理大型紋理 Unswizzling 的資料上限,數值越高載入紋理的速度越快,但會增加畫面延遲;數值較低則能降低 GPU 負載,不過有可能導致至紋理載入不完全甚至突然出現 + GPU Unswizzle Chunk 大小 + 設定 3D 紋理每批次處理的深度切片數量,增加此數值可提升規格較佳 GPU 的處理效率,但在較低規格的硬體上可能導致卡頓或驅動程式逾時 + 預設 擴充功能 擴展動態狀態 - 控制擴展動態狀態中可使用的功能數量。較高的數值可根據驅動程式支援的動態狀態減少管線編譯的次數。 + 控制擴展動態狀態中可使用的功能數量,較高的數值可根據驅動程式支援的動態狀態減少管線編譯的次數 停用 頂點輸入動態狀態 啟用此功能可提供更彈性的頂點輸入處理方式,並可能減少頂點/緩衝區的管線編譯時間 @@ -599,25 +617,25 @@ 子母畫面 位於背景時最小化視窗 - 解析度縮放到2倍或更高可能會導致問題,並可能顯著降低設備速度 + 解析度縮放到 2 倍或更高可能會導致問題,並可能顯著降低設備速度 CPU 時脈 使用自動存根 - 自動補上缺少的服務和功能。可提高相容性但可能導致當機和穩定性問題。 + 自動補上缺少的服務和功能,可提高相容性但可能導致當機和穩定性問題 GPU API 圖形偵錯 將圖形 API 設為慢速偵錯模式 BCn 區塊壓縮紋理修補 - 覆寫 Adreno GPU 的 BCn 紋理格式自動偵測。通常會根據 Android 版本自動偵測(會在API 28 以上自動啟用) + 覆寫 Adreno GPU 的 BCn 紋理格式自動偵測。通常會根據 Android 版本自動偵測(會在 API 28 以上的裝置自動啟用) Fastmem 日誌 按行寫入偵錯日誌 - 在每行寫入時重新整理偵錯日誌,讓程式在當機或閃退時更容易偵錯。 + 寫入一行日誌就立刻更新偵錯記錄,讓程式在當機或畫面凍結時更容易偵錯 啟用更詳細的日誌記錄 將日誌檔的最大檔案限制從 100MB 提高到 1GB 日誌過濾器 @@ -626,21 +644,21 @@ GPU 日誌 記錄層級 - GPU 日誌的詳細層級 (越高日誌越詳細,不過會更容易過熱) + GPU 日誌的詳細層級(越高日誌越詳細,不過會更容易過熱) 記錄 Vulkan API 呼叫 在環形緩衝區中記錄所有 Vulkan API 呼叫 傾印 SPIR-V 著色器 - 將重新編譯後的 SPIR-V 二進位檔案 (.spv) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 spirv-dis/spirv-cross/spirv-val 進行檢查 + 將重新編譯後的 SPIR-V 二進位檔案(.spv)儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 spirv-dis/spirv-cross/spirv-val 進行檢查 傾印客體(Maxwell)著色器 - 將 Maxwell 客體著色器位元組碼檔案 (.ash) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 nvdisasm 進行檢查 + 將 Maxwell 客體著色器位元組碼檔案(.ash)儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 nvdisasm 進行檢查 傾印 Maxwell 巨集 - 將 Maxwell 巨集程式檔案 (.macro) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 envydis 進行檢查 + 將 Maxwell 巨集程式檔案(.macro)儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 envydis 進行檢查 追蹤 GPU 記憶體 監控 GPU 記憶體的分配和釋放 驅動程式偵錯資料 擷取驅動程式的特定偵錯資料(例如 Turnip breadcrumbs) 環形緩衝區大小 - 追蹤的近期 Vulkan 呼叫數量(預設為 512 ) + 追蹤的近期 Vulkan 呼叫數量(預設為 512) 64 至 4096 個項目 一般 @@ -670,7 +688,7 @@ 靈敏度 無感帶 用按鈕、按鍵來模擬搖桿 - 修改按鈕模擬左/右搖桿的值,視遊戲不同而有所差異\n(模擬左搖桿時,若將此數值設為50,則按下按鈕時遊戲裡的人物可能只會走動而不是奔跑;模擬右搖桿時所使用的值則是會影響到轉視角的速度,數值越高代表使用的模擬值越大) + 修改按鈕模擬左/右搖桿的值,視遊戲不同而有所差異\n(模擬左搖桿時,若將此數值設為 50,則按下按鈕時遊戲裡的人物可能只會走動而不是奔跑;模擬右搖桿時所使用的值則是會影響到轉視角的速度,數值越高代表使用的模擬值越大) 肩部按鍵 左 SL 左 SR @@ -720,7 +738,7 @@ 按下一個按鈕或肩部按鍵/移動搖桿 將方向鍵配對到 %1$s 配對到 %1$s - 導入設定檔失敗 + 匯入設定檔失敗 儲存設定檔失敗 重設控制器配對 您確定要將此控制器的所有配對重設為預設值?此動作無法復原 @@ -741,21 +759,21 @@ 自動 提交 無 - 導入 - 導出 - 導出失敗 - 導入失敗 + 匯入 + 匯出 + 匯出失敗 + 匯入失敗 正在取消 安裝 下載 刪除 編輯 - 已成功導入 - 已成功導出 + 已成功匯入 + 已成功匯出 開始 全域設定 個別遊戲設定 - 導入完成 + 匯入完成 使用全域設定 操作已成功完成 確認 @@ -786,9 +804,9 @@ 安裝中… 最新 推薦驅動程式: - GPU型號 - 不支援的GPU - 您的GPU不支援驅動程式注入。不建議嘗試設定自訂驅動程式。 + GPU 型號 + 不支援的 GPU + 您的 GPU 不支援驅動程式注入,不建議嘗試設定自訂驅動程式 設定 @@ -818,13 +836,13 @@ 成功遷移儲存資料 儲存資料遷移失敗 存檔目錄設定完成 - 所選取的位置已有儲存資料。是否要取代該檔案? + 所選取的位置已有儲存資料,是否要取代該檔案? 若要自訂路徑必須授予所有檔案的存取權 授予權限 NAND 路徑 設定 NAND 資料的自訂路徑 - 虛擬 SD卡 路徑 - 設定虛擬 SD卡 儲存空間的自訂路徑 + 虛擬SD卡路徑 + 設定虛擬SD卡儲存空間的自訂路徑 路徑設定成功 跳過 @@ -843,7 +861,7 @@ 切換模組、更新及 DLC 遊玩時間: 重設遊玩時間 - 將此遊戲的遊玩時間重設為0 + 將此遊戲的遊玩時間重設為 0 您確定要重設此遊戲的遊玩時間嗎? 遊玩時間已重設 修改遊玩時間 @@ -852,16 +870,16 @@ 小時 分鐘 秒 - 必須介於0到9999小時之間 - 必須介於0到59分鐘之間 - 必須介於0到59秒之間 - 成功更新遊玩時間 + 必須介於 0 到 9999 小時之間 + 必須介於 0 到 59 分鐘之間 + 必須介於 0 到 59 秒之間 + 成功修改遊玩時間 清除著色器快取 清除此遊戲所有已編譯的著色器快取 重新編譯著色器快取的過程中可能會導致遊戲有些微卡頓 著色器快取已成功清除 已刪除著色器快取 - 為了維持Vulkan管線驗證,Eden已自動清除所有著色器快取。為了預防閃退和圖形錯誤,這是必須的動作。因為著色器需要重新編譯,您在遊戲過程中可能會遇到些微卡頓的情況 + 為了維持Vulkan管線驗證,Eden 已自動清除所有著色器快取,若要預防閃退和圖形錯誤,這是必須的動作。因為著色器需要重新編譯,您在遊戲過程中可能會遇到些微卡頓的情況 附加元件:%1$s 儲存資料 管理此遊戲的儲存資料 @@ -893,8 +911,8 @@ 未完成文件完整性檢查 以下文件的完整性驗證失敗:\n %1$s 分享設定 - 導入設定 - 導出設定 + 匯入設定 + 匯出設定 分享設定失敗 @@ -910,12 +928,12 @@ 正在搜尋遊戲... - 找不到標題ID的遊戲: %1$s + 找不到標題 ID 的遊戲: %1$s 自訂設定失敗 無法套用自訂設定 %1$s: %2$s 使用預設設定啟動 啟動已取消 - 無法套用請求的設定。這可能是由於缺少GPU驅動程式或設定問題。 + 無法套用請求的設定,這可能是由於缺少 GPU 驅動程式或設定問題 已套用自訂設定 正在啟動 %1$s... 無法初始化遊戲 @@ -936,10 +954,10 @@ 未安裝所需的驅動程式: %s 無效的驅動程式檔案: %s - 無可用網路連線。請檢查您的網路連線並重試 - 需要GPU驅動程式 + 無可用網路連線,請檢查您的網路連線並重試 + 需要 GPU 驅動程式 這個遊戲的設定需要 \"%s\"驅動程式,而它並沒有安裝在您的裝置上\n\n要下載並安裝此驅動程式嗎? - 驅動程式下載已取消。沒有所需的驅動程式無法啟動遊戲。 + 驅動程式下載已取消,沒有所需的驅動程式無法啟動遊戲 下載 @@ -961,7 +979,7 @@ 暫停模擬 繼續模擬 疊加層/虛擬按鍵設定 - 導入Amiibo + 導入 Amiibo 觸控螢幕 鎖定側邊選單 解鎖側邊選單 @@ -969,12 +987,12 @@ Amiibo - 該amiibo已被移除 - 此遊戲目前沒有在尋找amiibo - 您所選擇的並不是正確的amiibo金鑰檔 + 該 amiibo 已被移除 + 此遊戲目前沒有在尋找 amiibo + 您所選擇的並不是正確的 amiibo 金鑰檔 選擇的檔案已在使用中 發生未知的錯誤 - 已導入Amiibo + Amiibo 已匯入 @@ -1003,13 +1021,44 @@ 臺灣 - 4GB (推薦) - 6GB (不穩定) - 8GB (不穩定) + 4GB(推薦) + 6GB(不穩定) + 8GB(不穩定) + + + 正常 + 加速 + 超頻 + + + 正常 + 加速 + 超頻 + + + 極小 (16MB) + 小(32MB) + 正常(128MB) + 大(256MB) + 極大(512MB) + + + 極小(4MB) + 小(8MB) + 正常(16MB) + 中(32MB) + 大(64MB) + + + 極小(32) + 小(64) + 正常(128) + 中(256) + 大(512) - 攝氏度 - 華氏度 + 攝氏 + 華氏 B @@ -1022,20 +1071,28 @@ 無 - 準確 + + 效能 + 品質 預設 不穩定 穩定 + + 預設 + 即時 + 平衡 + 準確 + 保守 積極 - 即時 (關閉) + 即時(關閉) Mailbox - FIFO (開啟) + FIFO(開啟) FIFO 寬鬆 @@ -1057,6 +1114,18 @@ 不合理 偵錯 + + Freedreno 設定 + GPU 驅動程式設定 + 預先提供的設定檔 + 目前使用的設定 + 進階設定 + 變數名稱 (例如: TU_DEBUG) + 變數值 + 新增變數 + 全部清除 + Freedreno 設定已儲存 + 已清除所有 Freedreno 變數 方向鍵 左搖桿 @@ -1124,4 +1193,4 @@ 授權 來自 AMD 的升級圖像品質 -
+
\ No newline at end of file diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 63352386a4..be367a2fcf 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -558,7 +558,6 @@ @string/gpu_fence_behavior_immediate @string/gpu_fence_behavior_balanced @string/gpu_fence_behavior_accurate - @string/gpu_fence_behavior_strict 0 @@ -591,6 +590,54 @@ 2 + + @string/gpu_texturesizeswizzle_verysmall + @string/gpu_texturesizeswizzle_small + @string/gpu_texturesizeswizzle_normal + @string/gpu_texturesizeswizzle_large + @string/gpu_texturesizeswizzle_verylarge + + + + 0 + 1 + 2 + 3 + 4 + + + + @string/gpu_swizzle_verylow + @string/gpu_swizzle_low + @string/gpu_swizzle_normal + @string/gpu_swizzle_medium + @string/gpu_swizzle_high + + + + 0 + 1 + 2 + 3 + 4 + + + + @string/gpu_swizzlechunk_verylow + @string/gpu_swizzlechunk_low + @string/gpu_swizzlechunk_normal + @string/gpu_swizzlechunk_medium + @string/gpu_swizzlechunk_high + + + + 0 + 1 + 2 + 3 + 4 + + @string/temperature_celsius @string/temperature_fahrenheit diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index bd0716013c..33e1eaa340 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -105,8 +105,6 @@ Synchronize the core tick speed to the maximum speed percentage to improve performance without altering the game\'s actual speed. Enable Host MMU Emulation This optimization speeds up memory accesses by the guest program. Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host\'s MMU. Disabling this forces all memory accesses to use Software MMU Emulation. - Run 36-bit games natively - Gives games built for the 36-bit address space the 39-bit layout so they can run with NCE instead of the JIT. Experimental: a game that relies on 36-bit addresses can crash. Debug knobs For development use only. 0 to 65535 @@ -348,7 +346,7 @@ Match motion estimation to the game Estimate motion at the resolution the game actually renders instead of the upscaled output. Costs nothing in accuracy, since upscaling adds no motion detail. Motion estimation resolution - Resolution of the optical flow pass, as a fraction of the game\'s render resolution. Lowering it is the cheapest way to reclaim performance. + Resolution of the optical flow pass, as a fraction of the output. Lowering it is the cheapest way to reclaim performance. Frame generation unavailable This GPU driver lacks the Vulkan memory model or half precision (float16) support that the Lossless Scaling shaders require. Optional. Provide your own Lossless.dll to enable frame generation later @@ -575,8 +573,6 @@ Pick how ASTC-compressed textures are decoded for rendering: CPU (slow, safe), GPU (fast, recommended), or CPU Async (no stutters, may cause issues) Sync Memory Operations - Pause CPU on GPU fence timeouts - When a game keeps timing out while waiting for the GPU, pauses every CPU thread until the GPU catches up. Disabling it keeps the other threads running, which can reduce stutter but may break games that rely on it. Ensures data consistency between compute and memory operations. This option should fix issues in some games, but may also reduce performance in some cases. Unreal Engine 4 games often see the most significant changes thereof. Disk shader cache Reduces stuttering by locally storing and loading generated shaders. @@ -586,14 +582,8 @@ This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations. Use reactive flushing Improves rendering accuracy in some games at the cost of performance. - Barrier feedback loops - Improves rendering of transparency effects in specific games at the cost of performance. Enable buffer history Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. - Enable GPU Buffer Readback - Unified Memory - Backs part of the emulated memory with GPU-shared buffers so textures are unswizzled directly from it, skipping a CPU copy. Needs 12 GB of RAM or more and a GPU with I/O coherency; it turns itself off otherwise. Takes effect on the next game launch. - Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload. Optimized Vertex Buffers Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below). @@ -611,6 +601,17 @@ Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path Use asynchronous shaders Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches. + GPU Unswizzle Settings + Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality. + Enable GPU Unswizzle + Disabled + GPU Unswizzle Max Texture Size + Sets the maximum size (MB) for GPU-based texture unswizzling. While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones. Adjust this to find the balance between GPU acceleration and CPU overhead. + GPU Unswizzle Stream Size + Sets the data limit per frame for unswizzling large textures. Higher values speed up texture loading at the cost of higher frame latency; lower values reduce GPU overhead but may cause visible texture pop-in. + GPU Unswizzle Chunk Size + Defines the number of depth slices processed per batch for 3D textures. Increasing this improves throughput efficiency on powerful GPUs but may cause stuttering or driver timeouts on weaker hardware. + Default Extensions @@ -1060,10 +1061,25 @@ Overclock + Very Small (16 MB) + Small (32 MB) + Normal (128 MB) + Large (256 MB) + Very Large (512 MB) + Very Low (4 MB) + Low (8 MB) + Normal (16 MB) + Medium (32 MB) + High (64 MB) + Very Low (32) + Low (64) + Normal (128) + Medium (256) + High (512) Celsius @@ -1118,7 +1134,6 @@ Immediate Balanced Accurate - Strict CPU diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index f71a2df6be..f8946cc0f6 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -14,21 +14,6 @@ #include "core/core_timing.h" #include "core/hle/kernel/k_event.h" -// See texture_cache/util.h -template -#if BOOST_VERSION >= 108100 || __GNUC__ > 12 -[[nodiscard]] boost::container::static_vector FixStaticVectorADL(const boost::container::static_vector& v) { - return v; -} -#else -[[nodiscard]] std::vector FixStaticVectorADL(const boost::container::static_vector& v) { - std::vector u; - for (auto const& e : v) - u.push_back(e); - return u; -} -#endif - namespace AudioCore::AudioIn { System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_) @@ -110,7 +95,7 @@ Result System::Start() { boost::container::static_vector buffers_to_flush{}; buffers.RegisterBuffers(buffers_to_flush); - session->AppendBuffers(FixStaticVectorADL(buffers_to_flush)); + session->AppendBuffers(buffers_to_flush); session->SetRingSize(static_cast(buffers_to_flush.size())); return ResultSuccess; @@ -155,7 +140,7 @@ void System::RegisterBuffers() { if (state == State::Started) { boost::container::static_vector registered_buffers{}; buffers.RegisterBuffers(registered_buffers); - session->AppendBuffers(FixStaticVectorADL(registered_buffers)); + session->AppendBuffers(registered_buffers); } } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3a6d2acf8a..5d1b30a549 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -76,7 +76,6 @@ add_library( logging.h lz4_compression.cpp lz4_compression.h - make_unique_for_overwrite.h math_util.h memory_detect.cpp memory_detect.h @@ -108,7 +107,8 @@ add_library( settings_setting.h slot_vector.h socket_types.h - spin_lock.h + sparse_large_vector.cpp + sparse_large_vector.h stb.cpp stb.h steady_clock.cpp @@ -137,8 +137,8 @@ add_library( uuid.cpp uuid.h vector_math.h - virtual_buffer.cpp - virtual_buffer.h + zbic_compression.cpp + zbic_compression.h zstd_compression.cpp zstd_compression.h fs/ryujinx_compat.h fs/ryujinx_compat.cpp @@ -149,6 +149,10 @@ add_library( net/net.h net/net.cpp container/unordered_map.h container/unordered_set.h) +set_source_files_properties(zbic_compression.cpp PROPERTIES + INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}" + COMPILE_OPTIONS "$<$:-Wno-unused-function;-Wno-missing-declarations;-Wno-shadow>") + if(WIN32) target_sources(common PRIVATE windows/timer_resolution.cpp windows/timer_resolution.h) diff --git a/src/common/cpu_features.cpp b/src/common/cpu_features.cpp index 2e02c47826..8ccb6847f7 100644 --- a/src/common/cpu_features.cpp +++ b/src/common/cpu_features.cpp @@ -244,7 +244,8 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept , ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0} , us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0} , ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0} - , rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1} + , rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1} + , rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0} , cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0} , gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0} , invariant{invariant_} @@ -291,7 +292,7 @@ bool WallClock::IsNative() const { } u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { - return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count(); + return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor); } #elif defined(HAS_NCE) namespace { @@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { const WallClock g_wall_clock = [] { #if defined(ARCHITECTURE_x86_64) auto const& caps = Common::g_cpu_caps; - return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency); + return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency); #elif defined(HAS_NCE) return WallClock(false, 1); #else diff --git a/src/common/cpu_features.h b/src/common/cpu_features.h index 7dcb550f8f..61aa2767ab 100644 --- a/src/common/cpu_features.h +++ b/src/common/cpu_features.h @@ -96,6 +96,7 @@ public: u64 ns_rdtsc_factor; u64 us_rdtsc_factor; u64 ms_rdtsc_factor; + u64 rdtsc_ns_integer; u64 rdtsc_ns_factor; u64 cntpct_rdtsc_factor; u64 gputick_rdtsc_factor; diff --git a/src/common/error.cpp b/src/common/error.cpp index 070c9a5c7e..1d67dad6a9 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp @@ -17,35 +17,33 @@ namespace Common { +// glibc, mlibc, musl, and newlib all define their own variants of strerror_r +// We don't need to use the preprocessor, we can just select depending on return type +template std::string HandleStrerrorR(T r, char *err_str); +template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; } +template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; } +template<> std::string HandleStrerrorR(int r, char *err_str) { + return std::string{r != 0 + ? "(strerror_r failed to format error)" + : err_str}; +} + std::string NativeErrorToString(int e) { #ifdef _WIN32 LPSTR err_str; - DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&err_str), 1, nullptr); - if (!res) { - return "(FormatMessageA failed to format error)"; + LPSTR(&err_str), 1, nullptr); + if (res) { + std::string ret(err_str); + LocalFree(err_str); + return ret; } - std::string ret(err_str); - LocalFree(err_str); - return ret; + return "(FormatMessageA failed to format error)"; #else char err_str[255]; -#if defined(__ANDROID__) || \ - (defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))) - // Thread safe (GNU-specific) - const char* str = strerror_r(e, err_str, sizeof(err_str)); - return std::string(str); -#else - // Thread safe (XSI-compliant) - int second_err = strerror_r(e, err_str, sizeof(err_str)); - if (second_err != 0) { - return "(strerror_r failed to format error)"; - } - return std::string(err_str); -#endif // GLIBC etc. + return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str); #endif // _WIN32 } diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 69eca732eb..0e7f28c5b9 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -9,7 +9,6 @@ #include "common/assert.h" #include "common/fiber.h" -#include "common/virtual_buffer.h" #include diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index b895aac54c..82ea8f6cb2 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include "common/container/unordered_map.h" @@ -480,28 +481,80 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), path.end()); - const bool absolute = !path.empty() && path[0] == type2; - std::vector parts; + std::string root; + std::string_view components{path}; + bool drive_relative = false; - for (const auto part : SplitPathComponents(path)) - { - if (part.empty() || part == ".") - continue; - if (part == ".." && !parts.empty() && parts.back() != "..") - parts.pop_back(); - else if (part != "..") parts.push_back(part); +#ifdef _WIN32 + const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2; + const bool drive = + path.size() > 1 && std::isalpha(static_cast(path[0])) && path[1] == ':'; + + if (network) { + root.assign(2, type2); + components.remove_prefix(2); + } else if (drive) { + root.assign(path.data(), 2); + components.remove_prefix(2); + if (!components.empty() && components.front() == type2) { + root += type2; + components.remove_prefix(1); + } else { + drive_relative = true; + } + } +#endif + + if (root.empty() && !components.empty() && components.front() == type2) { + root += type2; + components.remove_prefix(1); } - std::string resolved = absolute ? std::string(1, type2) : std::string{}; - for (std::size_t i = 0; i < parts.size(); ++i) - { - if (i != 0) + const auto path_parts = SplitPathComponents(components); + std::size_t root_component_count = 0; +#ifdef _WIN32 + if (network) { + root_component_count = 2; + + const auto is_unc = [](std::string_view part) { + return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') && + (part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c'); + }; + if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) { + root_component_count = 4; + } + } +#endif + + std::vector parts; + for (std::size_t i = 0; i < path_parts.size(); ++i) { + const auto part = path_parts[i]; + if (i < root_component_count) { + parts.push_back(part); + } else if (part.empty() || part == ".") { + continue; + } else if (part == "..") { + if (parts.size() > root_component_count) { + parts.pop_back(); + } + } else { + parts.push_back(part); + } + } + + const std::size_t root_length = root.size(); + std::string resolved = std::move(root); + for (std::size_t i = 0; i < parts.size(); ++i) { + if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative)) resolved += type2; resolved.append(parts[i].data(), parts[i].size()); } path = std::move(resolved); + if (!path.empty() && path.size() == root_length) { + return path; + } return std::string(RemoveTrailingSlash(path)); } diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 983e4917e8..e8fa5614ae 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -347,8 +347,9 @@ enum class DirectorySeparator { // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } [[nodiscard]] std::vector SplitPathComponentsCopy(std::string_view filename); -// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' -// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows +// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves +// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are +// preserved. [[nodiscard]] std::string SanitizePath( std::string_view path, DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index dd0780a283..75a9da80e9 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -178,6 +178,14 @@ public: Release(); } + void* Allocate(size_t size) { + auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (ptr == nullptr) { + LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError()); + } + return ptr; + } + void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { std::unique_lock lock{placeholder_mutex}; if (!IsNiechePlaceholder(virtual_offset, length)) { @@ -398,6 +406,10 @@ private: // For managarm: see https://github.com/managarm/managarm/issues/1370 #else // ^^^ Windows ^^^ vvv POSIX vvv +#ifndef MAP_NOCORE +#define MAP_NOCORE 0 +#endif + #ifdef ARCHITECTURE_arm64 static void* ChooseVirtualBase(size_t virtual_size) { @@ -422,7 +434,7 @@ static void* ChooseVirtualBase(size_t virtual_size) { // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here. void* map_pointer = mmap(reinterpret_cast(hint_address), virtual_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0); // If we successfully mapped, we're done. if (reinterpret_cast(map_pointer) == hint_address) { @@ -442,11 +454,11 @@ static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) { #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__) - void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0); + void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0); if (virtual_base != MAP_FAILED) return virtual_base; #endif - return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0); } #endif @@ -540,13 +552,13 @@ public: } if (use_anon) { LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones"); - backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0)); if (fd > 0) { fd = -1; close(fd); } } else { - backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0)); } if (backing_base == MAP_FAILED) { LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); @@ -570,6 +582,14 @@ public: Release(); } + void* Allocate(size_t size) { + auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (ptr == MAP_FAILED) { + LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno)); + } + return ptr; + } + void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { // Intersect the range with our address space. AdjustMap(&virtual_offset, &length); @@ -690,12 +710,10 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) { #if defined(__OPENORBIS__) || defined(__managarm__) LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); - fallback_buffer.emplace(backing_size); - backing_base = fallback_buffer->data(); + backing_base = static_cast(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); virtual_base = nullptr; #else // Try to allocate a fastmem arena. - // The implementation will fail with std::bad_alloc on errors. impl = std::make_unique(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); if (impl->Init()) { backing_base = impl->backing_base; @@ -706,16 +724,26 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) virtual_base_offset = virtual_base - impl->virtual_base; } } else { - impl.reset(); LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it"); - fallback_buffer.emplace(backing_size); - backing_base = fallback_buffer->data(); + fallback_buffer = true; + backing_base = static_cast(impl->Allocate(backing_size)); virtual_base = nullptr; + impl.reset(); } #endif } -HostMemory::~HostMemory() = default; +HostMemory::~HostMemory() { +#ifdef _WIN32 + if (fallback_buffer) { + VirtualFree(backing_base, backing_size, MEM_RELEASE); + } +#else + if (fallback_buffer) { + munmap(backing_base, backing_size); + } +#endif +} HostMemory::HostMemory(HostMemory&&) noexcept = default; diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 24f4670732..a7fd0532b7 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -10,7 +10,6 @@ #include #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/virtual_buffer.h" namespace Common { @@ -86,7 +85,7 @@ private: u8* virtual_base{}; size_t virtual_base_offset{}; // Windows requires it for kernels whom lack proper support for some functions! - std::optional> fallback_buffer; + bool fallback_buffer{false}; }; } // namespace Common diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h deleted file mode 100644 index 17f81bba4a..0000000000 --- a/src/common/make_unique_for_overwrite.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Common { - -template - requires(!std::is_array_v) -std::unique_ptr make_unique_for_overwrite() { - return std::unique_ptr(new T); -} - -template - requires std::is_unbounded_array_v -std::unique_ptr make_unique_for_overwrite(std::size_t n) { - return std::unique_ptr(new std::remove_extent_t[n]); -} - -template - requires std::is_bounded_array_v -void make_unique_for_overwrite(Args&&...) = delete; - -} // namespace Common diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 33fff39002..73278cfc59 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -13,39 +13,11 @@ PageTable::PageTable() = default; PageTable::~PageTable() noexcept = default; -bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, - Common::ProcessAddress address) const { - out_context->next_offset = GetInteger(address); - out_context->next_page = address / page_size; - - return this->ContinueTraversal(out_entry, out_context); -} - -bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { - // Setup invalid defaults. - out_entry->phys_addr = 0; - out_entry->block_size = page_size; - // Validate that we can read the actual entry. - if (auto const page = context->next_page; page < entries.size()) { - // Validate that the entry is mapped. - if (auto const paddr = entries[page].addr; paddr != 0) { - // Populate the results. - out_entry->phys_addr = paddr + context->next_offset; - context->next_page += 1; - context->next_offset += page_size; - return true; - } - } - context->next_page += 1; - context->next_offset += page_size; - return false; -} - -void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) { - auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits); - entries.resize(num_page_table_entries); +void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) { + auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits); + entries.ResizeAndClear(num_page_table_entries); current_address_space_width_in_bits = address_space_width_in_bits; - page_size = 1ULL << page_size_in_bits; + current_page_bits = page_bits; } } // namespace Common diff --git a/src/common/page_table.h b/src/common/page_table.h index 0dfd152331..a21fe5311b 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -9,22 +9,22 @@ #include #include "common/common_types.h" +#include "common/sparse_large_vector.h" #include "common/typed_address.h" -#include "common/virtual_buffer.h" namespace Common { enum class PageType : u8 { /// Page is unmapped and should cause an access error. - Unmapped, + Unmapped = 0b00, /// Page is mapped to regular memory. This is the only type you can get pointers to. - Memory, + Memory = 0b01, /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use /// the callbacks. - DebugMemory, + DebugMemory = 0b10, /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and /// invalidation - RasterizerCachedMemory, + RasterizerCachedMemory = 0b11, }; /** @@ -42,57 +42,86 @@ struct PageTable { u64 next_offset{}; }; - /// Number of bits reserved for attribute tagging. - /// This can be at most the guaranteed alignment of the pointers in the page table. - static constexpr int ATTRIBUTE_BITS = 2; + /// Masks out bits reserved for attribute tagging. + static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12; + + /// Specifies sign bit for page table entries. + static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset /** - * Pair of host pointer and page type attribute. - * This uses the lower bits of a given pointer to store the attribute tag. + * Atomic tuple of host pointer, page type, and block id. + * This uses the lower bits of a given pointer to store the attributes. * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method * call. In other words, they are guaranteed to be synchronized at all times. */ - class PageInfo { + class PageEntryData { public: + struct Data { + Data(bool marked_, PageType type_, u16 block_, u64 page_) + : marked(static_cast(marked_) & 0b1) + , type(static_cast(type_) & ((1ULL << 2) - 1)) + , block(static_cast(block_) & ((1ULL << 9) - 1)) + , page((page_ >> 12) & ((1ULL << 45) - 1)) + , block2((static_cast(block_) >> 9) & ((1ULL << 7) - 1)) {} + u64 marked : 1; + u64 type : 2; + u64 block : 9; + u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit + u64 block2 : 7; + }; + + [[nodiscard]] Data Raw() const noexcept { + return std::bit_cast(data_raw.load(std::memory_order_relaxed)); + } + /// Returns the page pointer - [[nodiscard]] uintptr_t Pointer() const noexcept { - return ExtractPointer(raw.load(std::memory_order_relaxed)); + [[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept { + return ExtractPointer(std::bit_cast(data_raw.load(std::memory_order_relaxed)), ignored_marked); } /// Returns the page type attribute [[nodiscard]] PageType Type() const noexcept { - return ExtractType(raw.load(std::memory_order_relaxed)); + return static_cast(std::bit_cast(data_raw.load(std::memory_order_relaxed)).type); + } + + /// Returns the block identifier. + [[nodiscard]] u16 Block() const noexcept { + return ExtractBlock(std::bit_cast(data_raw.load(std::memory_order_relaxed))); } /// Returns the page pointer and attribute pair, extracted from the same atomic read - [[nodiscard]] std::pair PointerType() const noexcept { - const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed); - return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)}; + [[nodiscard]] std::tuple PointerTypeBlock(bool ignore_marked = false) const noexcept { + const auto non_atomic_raw = std::bit_cast(data_raw.load(std::memory_order_relaxed)); + return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast(non_atomic_raw.type), ExtractBlock(non_atomic_raw)}; } - /// Returns the raw representation of the page information. - /// Use ExtractPointer and ExtractType to unpack the value. - [[nodiscard]] uintptr_t Raw() const noexcept { - return raw.load(std::memory_order_relaxed); + /// Write page info atomically + inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept { + data_raw.store(std::bit_cast(Data{marked, type, block, pointer})); } - /// Write a page pointer and type pair atomically - void Store(uintptr_t pointer, PageType type) noexcept { - raw.store(pointer | uintptr_t(type)); + inline void MarkRasterizerCached() noexcept { + data_raw.fetch_or(0b111); + } + + inline void MarkDebug(u64 ptr, u16 block) noexcept { + Store(true, PageType::DebugMemory, block, ptr); } /// Unpack a pointer from a page info raw representation - [[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept { - return raw & (~uintptr_t{0} << ATTRIBUTE_BITS); + [[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept { + return raw.marked && !ignore_marked ? 0 + // shift raw.page's fake sign bit to the actual sign bit, then sign extend + : ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12); } - /// Unpack a page type from a page info raw representation - [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept { - return static_cast(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1)); + [[nodiscard]] static u16 ExtractBlock(Data raw) noexcept { + return static_cast(raw.block | (raw.block2 << 9)); } private: - std::atomic raw; + std::atomic data_raw; + static_assert(sizeof(Data) == sizeof(std::atomic)); }; PageTable(); @@ -100,13 +129,8 @@ struct PageTable { PageTable(const PageTable&) = delete; PageTable& operator=(const PageTable&) = delete; - - PageTable(PageTable&&) noexcept = default; - PageTable& operator=(PageTable&&) noexcept = default; - - bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, - Common::ProcessAddress address) const; - bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const; + PageTable(PageTable&&) noexcept = delete; + PageTable& operator=(PageTable&&) noexcept = delete; /** * Resizes the page table to be able to accommodate enough pages within @@ -121,30 +145,14 @@ struct PageTable { return current_address_space_width_in_bits; } - bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr, - Common::ProcessAddress virt_addr) const { - if (virt_addr > (1ULL << this->GetAddressSpaceBits())) { - return false; - } - - *out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr); - return true; - } - /// Vector of memory pointers backing each page. An entry can only be non-null if the /// corresponding attribute element is of type `Memory`. - struct PageEntryData { - PageInfo ptr; - u64 block; - u64 addr; - u64 padding; - }; - VirtualBuffer entries; - static_assert(sizeof(PageEntryData) == 32); + SparseLargeVector entries; + static_assert(sizeof(PageEntryData) == 8); u8* fastmem_arena{}; std::size_t current_address_space_width_in_bits{}; - std::size_t page_size{}; + std::size_t current_page_bits{}; }; } // namespace Common diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index 8c1f566301..929a65609a 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h @@ -8,8 +8,7 @@ #include #include - -#include "common/make_unique_for_overwrite.h" +#include namespace Common { @@ -38,8 +37,9 @@ public: ScratchBuffer() = default; explicit ScratchBuffer(size_type initial_capacity) - : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, - buffer{Common::make_unique_for_overwrite(initial_capacity)} {} + : last_requested_size{initial_capacity} + , buffer_capacity{initial_capacity} + , buffer{std::make_unique_for_overwrite(initial_capacity)} {} ~ScratchBuffer() = default; ScratchBuffer(const ScratchBuffer&) = delete; @@ -64,7 +64,7 @@ public: /// The previously held data will remain intact. void resize(size_type size) { if (size > buffer_capacity) { - auto new_buffer = Common::make_unique_for_overwrite(size); + auto new_buffer = std::make_unique_for_overwrite(size); std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T)); buffer = std::move(new_buffer); buffer_capacity = size; @@ -77,7 +77,7 @@ public: void resize_destructive(size_type size) { if (size > buffer_capacity) { buffer_capacity = size; - buffer = Common::make_unique_for_overwrite(buffer_capacity); + buffer = std::make_unique_for_overwrite(buffer_capacity); } last_requested_size = size; } diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 7605305a04..3f5154026e 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -48,6 +48,7 @@ SWITCHABLE(AnisotropyMode, true); SWITCHABLE(AntiAliasing, false); SWITCHABLE(AspectRatio, true); SWITCHABLE(AstcDecodeMode, true); +SWITCHABLE(AstcRecompression, true); SWITCHABLE(AudioMode, true); SWITCHABLE(CpuBackend, true); SWITCHABLE(CpuAccuracy, true); @@ -177,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() { return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate; } -bool IsGPUFenceBehaviorStrict() { - return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict; -} - bool IsFastmemEnabled() { if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging) return bool(values.cpuopt_fastmem); diff --git a/src/common/settings.h b/src/common/settings.h index 59f3d448da..b57bec5c12 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -65,6 +65,7 @@ SWITCHABLE(AnisotropyMode, true); SWITCHABLE(AntiAliasing, false); SWITCHABLE(AspectRatio, true); SWITCHABLE(AstcDecodeMode, true); +SWITCHABLE(AstcRecompression, true); SWITCHABLE(AudioMode, true); SWITCHABLE(CpuBackend, true); SWITCHABLE(CpuAccuracy, true); @@ -170,8 +171,13 @@ struct Values { SwitchableSetting enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet}; // Audio - SwitchableSetting sink_id{linkage, AudioEngine::Auto, "output_engine", - Category::Audio, Specialization::RuntimeList}; + SwitchableSetting sink_id{linkage, +#ifdef __ANDROID__ + AudioEngine::Sdl3, +#else + AudioEngine::Auto, +#endif + "output_engine", Category::Audio, Specialization::RuntimeList}; SwitchableSetting audio_output_device_id{ linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList}; SwitchableSetting audio_input_device_id{ @@ -259,8 +265,6 @@ struct Values { Category::Cpu}; SwitchableSetting cpu_accuracy{linkage, CpuAccuracy::Auto, "cpu_accuracy", Category::Cpu}; - SwitchableSetting relocate_36bit_address_space{ - linkage, false, "relocate_36bit_address_space", Category::Cpu}; SwitchableSetting cpu_clock{linkage, CpuClock::Normal, "fast_cpu_time", @@ -473,13 +477,8 @@ struct Values { SwitchableSetting frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", Category::Renderer}; - SwitchableSetting use_asynchronous_gpu_emulation{linkage, -#ifdef __ANDROID__ - false, -#else - true, -#endif - "use_asynchronous_gpu_emulation", Category::Renderer}; + SwitchableSetting use_asynchronous_gpu_emulation{linkage, true, "use_asynchronous_gpu_emulation", + Category::Renderer}; // *nix platforms may have issues with the borderless windowed fullscreen mode. // Default to exclusive fullscreen on these platforms for now. SwitchableSetting fullscreen_mode{linkage, @@ -526,7 +525,7 @@ struct Values { SwitchableSetting gpu_fence_behavior{linkage, GpuFenceBehavior::Default, GpuFenceBehavior::Default, - GpuFenceBehavior::Strict, + GpuFenceBehavior::Accurate, "gpu_fence_behavior", Category::RendererAdvanced, Specialization::Default, @@ -538,7 +537,7 @@ struct Values { "vram_usage_mode", Category::RendererAdvanced}; - SwitchableSetting nvdec_emulation{linkage, NvdecEmulation::Gpu, + SwitchableSetting nvdec_emulation{linkage, NvdecEmulation::Cpu, "nvdec_emulation", Category::RendererAdvanced}; SwitchableSetting max_anisotropy{linkage, @@ -554,6 +553,22 @@ struct Values { "accelerate_astc", Category::RendererAdvanced}; + SwitchableSetting frame_pacing_mode{linkage, + FramePacingMode::Target_Auto, + FramePacingMode::Target_Auto, + FramePacingMode::Target_120, + "frame_pacing_mode", + Category::RendererAdvanced, + Specialization::Default, + true, + true}; + + SwitchableSetting astc_recompression{linkage, + AstcRecompression::Uncompressed, + "astc_recompression", + Category::RendererAdvanced}; + + SwitchableSetting sync_memory_operations{linkage, false, "sync_memory_operations", @@ -561,8 +576,6 @@ struct Values { Specialization::Default, true, true}; - SwitchableSetting stall_on_gpu_fence_wait{linkage, true, "stall_on_gpu_fence_wait", - Category::RendererAdvanced}; SwitchableSetting renderer_force_max_clock{linkage, false, "force_max_clock", Category::RendererAdvanced}; @@ -589,13 +602,8 @@ struct Values { "use_reactive_flushing", Category::RendererAdvanced}; - SwitchableSetting barrier_feedback_loops{linkage, - true, - "barrier_feedback_loops", - Category::RendererAdvanced, - Specialization::Default, - true, - true}; + SwitchableSetting barrier_feedback_loops{linkage, true, "barrier_feedback_loops", + Category::RendererAdvanced}; SwitchableSetting enable_buffer_history{linkage, false, @@ -645,21 +653,29 @@ struct Values { SwitchableSetting emulate_bgr565{linkage, false, "emulate_bgr565", Category::RendererHacks}; - SwitchableSetting rescale_hack{linkage, false, "rescale_hack", - Category::RendererHacks}; - SwitchableSetting enable_gpu_buffer_readback{linkage, - false, - "enable_gpu_buffer_readback", - Category::RendererAdvanced, - Specialization::Default, - true, - true}; - SwitchableSetting use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", Category::RendererHacks}; - SwitchableSetting use_unified_memory{linkage, false, "use_unified_memory", - Category::RendererHacks}; + SwitchableSetting gpu_unswizzle_texture_size{linkage, + GpuUnswizzleSize::Large, + "gpu_unswizzle_texture_size", + Category::RendererHacks, + Specialization::Default}; + + SwitchableSetting gpu_unswizzle_stream_size{linkage, + GpuUnswizzle::Medium, + "gpu_unswizzle_stream_size", + Category::RendererHacks, + Specialization::Default}; + + SwitchableSetting gpu_unswizzle_chunk_size{linkage, + GpuUnswizzleChunk::Medium, + "gpu_unswizzle_chunk_size", + Category::RendererHacks, + Specialization::Default}; + + SwitchableSetting gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled", + Category::RendererHacks}; SwitchableSetting dyna_state{linkage, #if defined(__ANDROID__) @@ -913,7 +929,7 @@ struct Values { // Network Setting network_interface{linkage, std::string(), "network_interface", Category::Network}; - SwitchableSetting airplane_mode{linkage, false, "airplane_mode", Category::Network}; + SwitchableSetting airplane_mode{linkage, true, "airplane_mode", Category::Network}; // WebService Setting web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url", @@ -953,7 +969,6 @@ bool IsDMALevelSafe(); bool IsGPUFenceBehaviorDefault(); bool IsGPUFenceBehaviorBalanced(); bool IsGPUFenceBehaviorAccurate(); -bool IsGPUFenceBehaviorStrict(); bool IsFastmemEnabled(); void SetNceEnabled(bool is_64bit); diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 60e2b55742..eaf3ec541d 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -130,12 +130,14 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16); ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); +ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); +ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120); ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); ENUM(VramUsageMode, Conservative, Aggressive); ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV); ENUM(GpuAccuracy, Low, High); ENUM(DmaAccuracy, Default, Unsafe, Safe); -ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict); +ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate); ENUM(CpuBackend, Dynarmic, Nce); ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging); ENUM(CpuClock, Normal, Boost, Overclock) @@ -148,9 +150,12 @@ ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, Scale ENUM(AntiAliasing, None, Fxaa, Smaa); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); -ENUM(AppletMode, HLE, LLE); +ENUM(AppletMode, HLE, LLE, Disabled); ENUM(SpirvOptimizeMode, Never, OnLoad, Always); ENUM(GpuClock, Normal, Boost, Overclock) +ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge) +ENUM(GpuUnswizzle, VeryLow, Low, Normal, Medium, High) +ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High) ENUM(TemperatureUnits, Celsius, Fahrenheit) ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3); ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All) diff --git a/src/common/sparse_large_vector.cpp b/src/common/sparse_large_vector.cpp new file mode 100644 index 0000000000..eee8ff6d18 --- /dev/null +++ b/src/common/sparse_large_vector.cpp @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +/* virtual_buffer.cpp */ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _WIN32 +#include +#include +#include +#else +#include +#endif + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/sparse_large_vector.h" + +namespace Common { + +#ifdef _WIN32 +static std::vector> vector_regions {}; +static std::mutex vector_regions_mutex {}; + +// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error +static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) { + DWORD code = info->ExceptionRecord->ExceptionCode; + u64 exception_addr = reinterpret_cast(info->ExceptionRecord->ExceptionInformation[1]); + + if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) { + // Not our problem + return EXCEPTION_CONTINUE_SEARCH; + } + + u64 addr = 0, addr2 = 0; + + { + std::lock_guard lock(vector_regions_mutex); + for (auto region: vector_regions) { + auto addr_shifted = exception_addr >> HostPageBits; + if (region.first <= addr_shifted && addr_shifted <= region.second) { + addr = addr_shifted; + } + + // Page-boundary accesses + if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) { + addr2 = addr_; + } + + if (addr != 0 || addr2 != 0) { + break; + } + } + } + + if (addr == 0 && addr2 == 0) { + // Not our problem + return EXCEPTION_CONTINUE_SEARCH; + } + + LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr); + + // Commit this region + if (addr != 0) { + if (!CommitVectorPage(addr << HostPageBits, false)) { + return EXCEPTION_CONTINUE_SEARCH; + } + } + // Commit next region if needed + if (addr2 != 0) { + if (!CommitVectorPage(addr2 << HostPageBits, false)) { + return EXCEPTION_CONTINUE_SEARCH; + } + } + + return EXCEPTION_CONTINUE_EXECUTION; +} + +bool CommitVectorPage(uintptr_t addr, bool write) noexcept { + MEMORY_BASIC_INFORMATION info {}; + auto res = VirtualQuery(reinterpret_cast(addr), &info, sizeof(info)); + if (res == 0) { + LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError()); + } else if (info.State == MEM_COMMIT) { + DWORD old_protect {}; + auto perm = write ? PAGE_READWRITE : PAGE_READONLY; + if (!VirtualProtect(reinterpret_cast(addr), HostPageSize, perm, &old_protect)) { + LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError()); + return false; + } + return true; + } else if (info.State != MEM_RESERVE) { + LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State); + return false; + } + + auto perm = write ? PAGE_READWRITE : PAGE_READONLY; + void* res2 = VirtualAlloc(reinterpret_cast(addr), HostPageSize, MEM_COMMIT, perm); + if (res2 == nullptr) { + LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError()); + return false; + } + + return true; +} +#endif + +#ifndef MAP_NOCORE +#define MAP_NOCORE 0 +#endif +#ifndef MADV_FREE +#define MADV_FREE MADV_DONTNEED +#endif + +void DecommitVectorPage(uintptr_t base) noexcept { +#if defined(_WIN32) + VirtualFree(reinterpret_cast(base), HostPageSize, MEM_DECOMMIT); +#elif defined(__linux__) + // Linux's MADV_DONTNEED zeros out pages for us + madvise(reinterpret_cast(base), HostPageSize, MADV_DONTNEED); +#else + madvise(reinterpret_cast(base), HostPageSize, MADV_FREE); + std::memset(reinterpret_cast(base), 0, HostPageSize); +#endif +} + +void* AllocateMemoryPages(std::size_t size) noexcept { + if (auto page = HostPageSize; size % page != 0) { + LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page); + size = AlignUp(size, page); + } + +#ifdef _WIN32 + // We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually + void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); + + if (base != nullptr) { + std::lock_guard lock(vector_regions_mutex); + vector_regions.emplace_back(reinterpret_cast(base) >> HostPageBits, (reinterpret_cast(base) + size) >> HostPageBits); + + static std::once_flag flag; + std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); }); + } else { + // Try committing everything instead?? + LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError()); + base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); + } + ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError()); +#else + void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0); + if (base == MAP_FAILED) + base = nullptr; + ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno)); +#endif + return base; +} + +void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept { + if (auto page = HostPageSize; size % page != 0) { + size = AlignUp(size, page); + } + if (!base) + return; +#ifdef _WIN32 + std::lock_guard lock(vector_regions_mutex); + std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast(base); }); + ASSERT(VirtualFree(base, 0, MEM_RELEASE)); +#else + ASSERT(munmap(base, size) == 0); +#endif +} + +} // namespace Common diff --git a/src/common/sparse_large_vector.h b/src/common/sparse_large_vector.h new file mode 100644 index 0000000000..8496c28cef --- /dev/null +++ b/src/common/sparse_large_vector.h @@ -0,0 +1,214 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +/* virtual_buffer.h */ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "common/alignment.h" +#include "common/assert.h" + +namespace Common { + +#ifdef _WIN32 +constexpr u64 HostPageSize = 0x1000; +constexpr u64 HostPageBits = 12; +constexpr u64 HostPageMask = ~(HostPageSize - 1); +bool CommitVectorPage(uintptr_t addr, bool write) noexcept; +#else +inline const u64 HostPageSize = sysconf(_SC_PAGESIZE); +inline const u64 HostPageBits = std::countr_zero(HostPageSize); +inline const u64 HostPageMask = ~(HostPageSize - 1); +#endif + +void* AllocateMemoryPages(std::size_t size) noexcept; +void FreeMemoryPages(void* base, std::size_t size) noexcept; +void DecommitVectorPage(uintptr_t base) noexcept; + +/// A large page-aligned buffer that has optimized memory usage for zero-writes. +template + // MSVC doesn't regard structs with atomics as trivially copyable + // requires std::is_trivially_copyable_v +class SparseLargeVector final { +public: + constexpr SparseLargeVector() = default; + + explicit SparseLargeVector(std::size_t count) noexcept + : alloc_size{count * sizeof(T)} + { + base_ptr = static_cast(AllocateMemoryPages(alloc_size)); + + // each item in vector holds information for 64 pages + auto denom = HostPageSize * 64; + committed_pages = std::vector>((alloc_size + denom - 1) / denom); + } + + ~SparseLargeVector() noexcept { + FreeMemoryPages(base_ptr, alloc_size); + } + + SparseLargeVector(const SparseLargeVector&) = delete; + SparseLargeVector& operator=(const SparseLargeVector&) = delete; + SparseLargeVector(SparseLargeVector&& other) = delete; + SparseLargeVector& operator=(SparseLargeVector&& other) = delete; + + void ResizeAndClear(std::size_t count) noexcept { + if (auto const new_size = count * sizeof(T); new_size != alloc_size) { + FreeMemoryPages(base_ptr, alloc_size); + alloc_size = new_size; + base_ptr = static_cast(AllocateMemoryPages(alloc_size)); + + auto denom = HostPageSize * 64; + committed_pages = std::vector>((alloc_size + denom - 1) / denom); + } + } + + /// Returns a reference to the value of the requested index and allocates memory if needed. + T& GetAndFault(std::size_t index) noexcept { + if (index > alloc_size / sizeof(T)) { + UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index); + } + + if (!IsCommittedPage(index) && !CommitPage(index)) { + UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index); + } + return base_ptr[index]; + } + + /// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object. + const T& GetOrDefault(std::size_t index) const { +#ifdef _WIN32 + if (!IsCommittedPage(index)) { + return *reinterpret_cast(&default_val); + } +#endif + // On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated. + return base_ptr[index]; + } + + void Set(std::size_t index, const T& value) noexcept { + if (index > alloc_size / sizeof(T)) { + LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index); + return; + } + if (IsCommittedPage(index) || CommitPage(index)) + base_ptr[index] = value; + } + + void ZeroRegion(std::size_t start, std::size_t end_) noexcept { + u64 base = reinterpret_cast(&base_ptr[start]); + const u64 end = reinterpret_cast(&base_ptr[end_]); + + const u64 end_page = AlignUp(base, HostPageSize); + const u64 first_size = (std::min)(end_page, end) - base; + + if (IsCommittedPage(start)) { + std::memset(reinterpret_cast(base), 0, first_size); + } + + if (end <= end_page) + return; + + base = end_page; + + for (u64 page = base; page < end; page += HostPageSize) { + auto index = (page - reinterpret_cast(base_ptr)) / sizeof(T); + if (!IsCommittedPage(index)) { + continue; + } + + if (end - page >= HostPageSize) { + DecommitPage(index); + } else { + std::memset(reinterpret_cast(page), 0, end - page); + } + } + } + + constexpr void CommitRegion(size_t index, size_t end_) { + const u64 base = static_cast(index) * sizeof(T); + const u64 end = static_cast(end_) * sizeof(T); + + for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) { + if (!IsCommittedPage(page / sizeof(T))) { + CommitPage(page / sizeof(T)); + } + } + } + + constexpr T& GetUnchecked(size_t index) { + return base_ptr[index]; + } + + [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { + return GetOrDefault(index); + } + + [[nodiscard]] constexpr const T* data() const noexcept { + return base_ptr; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { + return alloc_size / sizeof(T); + } + +private: + [[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept { + if (index > alloc_size / sizeof(T)) { + LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index); + return false; + } + + auto page = (index * sizeof(T)) >> HostPageBits; + auto val = committed_pages[page >> 6].load(std::memory_order_acquire); + return (val >> (page & 63)) & 1; + } + + constexpr bool CommitPage(std::size_t index) noexcept { + auto page_index = (index * sizeof(T)) >> HostPageBits; + auto page = reinterpret_cast(base_ptr + index) & HostPageMask; +#if defined(_WIN32) + if (!CommitVectorPage(page, true)) { + return false; + } +#else + if (mprotect(reinterpret_cast(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) { + LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno)); + return false; + } +#endif + + committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release); + return true; + } + + constexpr void DecommitPage(std::size_t index) noexcept { + auto page_index = (index * sizeof(T)) >> HostPageBits; + auto page = reinterpret_cast(base_ptr + index) & HostPageMask; + + committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release); + DecommitVectorPage(page); + } + + std::size_t alloc_size{}; + T* base_ptr{}; + + std::vector> committed_pages{}; +#ifdef _WIN32 + const std::array default_val{}; +#endif +}; + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h deleted file mode 100644 index 93d507ae80..0000000000 --- a/src/common/spin_lock.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#ifdef _MSC_VER -#include -#elif defined(ARCHITECTURE_x86_64) -#include -#endif -#include - -namespace Common { - -/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the -/// supervisor. Should be used on short sequences of code. -struct SpinLock { - SpinLock() noexcept = default; - SpinLock(const SpinLock&) noexcept = delete; - SpinLock& operator=(const SpinLock&) noexcept = delete; - SpinLock(SpinLock&&) noexcept = delete; - SpinLock& operator=(SpinLock&&) noexcept = delete; - - inline void lock() noexcept { - while (lck.test_and_set(std::memory_order_acquire)) { -#if defined(ARCHITECTURE_x86_64) - _mm_pause(); -#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER) - __yield(); -#elif defined(ARCHITECTURE_arm64) - asm("yield"); -#endif - } - } - - inline void unlock() noexcept { - lck.clear(std::memory_order_release); - } - - [[nodiscard]] inline bool try_lock() noexcept { - return !lck.test_and_set(std::memory_order_acquire); - } - - std::atomic_flag lck = ATOMIC_FLAG_INIT; -}; - -} // namespace Common diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 51fe0abaa5..4b992c5df8 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp @@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() { return uuid; } -UUID UUID::MakeRFC4122V5(std::span sha1) { +UUID UUID::MakeRFC4122V5(std::span sha1) { UUID uuid{}; - std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID)); + std::memcpy(&uuid.uuid, sha1.data(), sha1.size()); uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF); return uuid; diff --git a/src/common/uuid.h b/src/common/uuid.h index 3aa3212dd7..465b5be58d 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -104,7 +104,7 @@ struct UUID { /// @returns A random UUID that is RFC 4122 Version 4 compliant. [[nodiscard]] static UUID MakeRandomRFC4122V4(); - [[nodiscard]] static UUID MakeRFC4122V5(std::span sha1); + [[nodiscard]] static UUID MakeRFC4122V5(std::span sha1); friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; }; diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp deleted file mode 100644 index 8a4265e3e6..0000000000 --- a/src/common/virtual_buffer.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#ifdef _WIN32 -#include -#else -#include -#endif - -#include "common/assert.h" -#include "common/virtual_buffer.h" - -namespace Common { - -void* AllocateMemoryPages(std::size_t size) noexcept { -#ifdef _WIN32 - void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (base == nullptr) { - // Probably failing to reserve is less likely than failing to commit - base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); - } -#else - void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - if (base == MAP_FAILED) - base = nullptr; -#endif - ASSERT(base); - return base; -} - -void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept { - if (!base) - return; -#ifdef _WIN32 - ASSERT(VirtualFree(base, 0, MEM_RELEASE)); -#else - ASSERT(munmap(base, size) == 0); -#endif -} - -} // namespace Common diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h deleted file mode 100644 index d6386e2a4d..0000000000 --- a/src/common/virtual_buffer.h +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -namespace Common { - -void* AllocateMemoryPages(std::size_t size) noexcept; -void FreeMemoryPages(void* base, std::size_t size) noexcept; - -template -class VirtualBuffer final { -public: - // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible - // using std::atomic_ref once libc++ has support for it - // static_assert( - // std::is_trivially_constructible_v, - // "T must be trivially constructible, as non-trivial constructors will not be executed " - // "with the current allocator"); - - constexpr VirtualBuffer() = default; - explicit VirtualBuffer(std::size_t count) noexcept - : alloc_size{count * sizeof(T)} - { - base_ptr = reinterpret_cast(AllocateMemoryPages(alloc_size)); - } - - ~VirtualBuffer() noexcept { - FreeMemoryPages(base_ptr, alloc_size); - } - - VirtualBuffer(const VirtualBuffer&) = delete; - VirtualBuffer& operator=(const VirtualBuffer&) = delete; - - VirtualBuffer(VirtualBuffer&& other) noexcept - : alloc_size{std::exchange(other.alloc_size, 0)} - , base_ptr{std::exchange(other.base_ptr, nullptr)} - {} - - VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { - alloc_size = std::exchange(other.alloc_size, 0); - base_ptr = std::exchange(other.base_ptr, nullptr); - return *this; - } - - void resize(std::size_t count) noexcept { - if (auto const new_size = count * sizeof(T); new_size != alloc_size) { - FreeMemoryPages(base_ptr, alloc_size); - alloc_size = new_size; - base_ptr = reinterpret_cast(AllocateMemoryPages(alloc_size)); - } - } - - [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { - return base_ptr[index]; - } - - [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { - return base_ptr[index]; - } - - [[nodiscard]] constexpr T* data() noexcept { - return base_ptr; - } - - [[nodiscard]] constexpr const T* data() const noexcept { - return base_ptr; - } - - [[nodiscard]] constexpr std::size_t size() const noexcept { - return alloc_size / sizeof(T); - } - -private: - std::size_t alloc_size{}; - T* base_ptr{}; -}; - -} // namespace Common diff --git a/src/common/zbic_compression.cpp b/src/common/zbic_compression.cpp new file mode 100644 index 0000000000..5e0e4d947b --- /dev/null +++ b/src/common/zbic_compression.cpp @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "common/zbic_compression.h" + +#define ZSTD_ZBIC_SUPPORT 1 +#define ZSTDLIB_VISIBLE static +#define ZSTDLIB_HIDDEN static +#define ZSTDERRORLIB_VISIBLE static +#define ZSTDERRORLIB_HIDDEN static +#undef ZSTD_MULTITHREAD + +#if defined(__ANDROID__) +#undef _GNU_SOURCE +#endif + +#include "zstd.h" +#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol +#include "zstd.c" +#undef g_ZSTD_threading_useless_symbol + +namespace Common::Compression { + +bool IsZBIC(std::span src) { + if (src.size() < sizeof(u32)) { + return false; + } + u32 magic = 0; + std::memcpy(&magic, src.data(), sizeof(u32)); + return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC") +} + +int DecompressDataZBIC(std::span dst, std::span src) { + if (dst.empty() || src.empty()) { + return -1; + } + const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size()); + if (ZSTD_isError(res)) { + return -1; + } + return static_cast(res); +} + +} // namespace Common::Compression diff --git a/src/common/zbic_compression.h b/src/common/zbic_compression.h new file mode 100644 index 0000000000..c36a559388 --- /dev/null +++ b/src/common/zbic_compression.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include "common/common_types.h" + +namespace Common::Compression { + +[[nodiscard]] bool IsZBIC(std::span src); + +[[nodiscard]] int DecompressDataZBIC(std::span dst, std::span src); + +} // namespace Common::Compression diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index cb6ec171b5..7665482ee6 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -46,4 +49,10 @@ std::vector DecompressDataZSTD(std::span compressed) { return decompressed; } +std::size_t DecompressDataZSTD(std::span compressed, std::span output) { + const std::size_t result = + ZSTD_decompress(output.data(), output.size(), compressed.data(), compressed.size()); + return ZSTD_isError(result) ? 0 : result; +} + } // namespace Common::Compression diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h index a5ab2d05bd..819d7d201a 100644 --- a/src/common/zstd_compression.h +++ b/src/common/zstd_compression.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -42,4 +45,15 @@ namespace Common::Compression { */ [[nodiscard]] std::vector DecompressDataZSTD(std::span compressed); +/** + * Decompresses a single Zstandard frame into a caller-provided buffer of known size. + * + * @param compressed the compressed source memory region. + * @param output the destination buffer. Its size is the maximum number of bytes to write. + * + * @return the number of bytes written, or 0 if the frame is invalid or doesn't fit. An empty + * frame also returns 0. + */ +[[nodiscard]] std::size_t DecompressDataZSTD(std::span compressed, std::span output); + } // namespace Common::Compression diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9cc2792a8c..1b578f67ba 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,6 +23,8 @@ add_library(core STATIC core_timing.h cpu_manager.cpp cpu_manager.h + launch_params.cpp + launch_params.h crypto/aes_util.cpp crypto/aes_util.h crypto/ctr_encryption_layer.cpp @@ -31,6 +33,8 @@ add_library(core STATIC crypto/encryption_layer.h crypto/key_manager.cpp crypto/key_manager.h + crypto/ncz_decompressor.cpp + crypto/ncz_decompressor.h crypto/partition_data_manager.cpp crypto/partition_data_manager.h crypto/xts_encryption_layer.cpp @@ -796,6 +800,8 @@ add_library(core STATIC hle/service/ns/application_manager_interface.h hle/service/ns/application_version_interface.cpp hle/service/ns/application_version_interface.h + hle/service/ns/async_result.cpp + hle/service/ns/async_result.h hle/service/ns/content_management_interface.cpp hle/service/ns/content_management_interface.h hle/service/ns/develop_interface.cpp @@ -810,8 +816,6 @@ add_library(core STATIC hle/service/ns/ecommerce_interface.h hle/service/ns/factory_reset_interface.cpp hle/service/ns/factory_reset_interface.h - hle/service/ns/i_async_result.cpp - hle/service/ns/i_async_result.h hle/service/ns/language.cpp hle/service/ns/language.h hle/service/ns/ns.cpp @@ -1203,6 +1207,9 @@ endif() target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS}) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) +if (MSVC) + target_link_libraries(core PRIVATE getopt) +endif() if (BOOST_NO_HEADERS) target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc) @@ -1238,7 +1245,8 @@ if (HAS_NCE) arm/nce/interpreter_visitor.cpp arm/nce/interpreter_visitor.h arm/nce/patcher.cpp - arm/nce/patcher.h) + arm/nce/patcher.h + arm/nce/visitor_base.h) target_link_libraries(core PRIVATE merry::oaknut) endif() diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 97284efa4c..a59aa3cac3 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} -u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) { - CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); - return m_memory.Read8(vaddr); -} -u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) { - CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); - return m_memory.Read16(vaddr); -} -u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) { - CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); - return m_memory.Read32(vaddr); -} -u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) { - CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); - return m_memory.Read64(vaddr); +u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) { + CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); + switch (size) { + case sizeof(u64): return m_memory.Read64(vaddr); + case sizeof(u32): return m_memory.Read32(vaddr); + case sizeof(u16): return m_memory.Read16(vaddr); + case sizeof(u8): return m_memory.Read8(vaddr); + default: UNREACHABLE(); + } } std::optional DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { @@ -50,27 +44,17 @@ std::optional DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; } -void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) { - if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { - m_memory.Write8(vaddr, value); +void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) { + if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { + switch (size) { + case sizeof(u64): return m_memory.Write64(vaddr, value); + case sizeof(u32): return m_memory.Write32(vaddr, u32(value)); + case sizeof(u16): return m_memory.Write16(vaddr, u16(value)); + case sizeof(u8): return m_memory.Write8(vaddr, u8(value)); + default: UNREACHABLE(); + } } } -void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) { - if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { - m_memory.Write16(vaddr, value); - } -} -void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) { - if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { - m_memory.Write32(vaddr, value); - } -} -void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) { - if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { - m_memory.Write64(vaddr, value); - } -} - bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) { return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && m_memory.WriteExclusive8(vaddr, value, expected); @@ -172,12 +156,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) { if (page_table) { constexpr size_t PageBits = 12; constexpr size_t NumPageTableEntries = 1 << (32 - PageBits); - constexpr size_t PageLog2Stride = 5; - static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData)); - config.page_table = reinterpret_cast*>(page_table->entries.data()); - config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; - config.page_table_log2_stride = PageLog2Stride; + // Dynarmic will not write to the page table, const_cast is safe here + config.page_table = reinterpret_cast*>( + const_cast(page_table->entries.data())); + config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; + config.page_table_marked_bit = uint8_t(0); config.absolute_offset_page_table = true; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.only_detect_misalignment_via_page_table_on_page_boundary = true; @@ -188,6 +172,13 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) { config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; config.recompile_on_exclusive_fastmem_failure = true; + + if (reinterpret_cast(m_system.DeviceMemory().buffer.BackingBasePointer() + + Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { + // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, + // we have to manually sign extend when our actual pointer is negative. + config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); + } } // Multi-process state @@ -420,6 +411,7 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) { } void ArmDynarmic32::ClearInstructionCache() { + m_cb->last_code_addr = u64(-1); m_jit->ClearCache(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 623597152f..22655dff47 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -30,18 +30,12 @@ class System; class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process); - u8 MemoryRead8(u32 vaddr) override; - u16 MemoryRead16(u32 vaddr) override; - u32 MemoryRead32(u32 vaddr) override; - u64 MemoryRead64(u32 vaddr) override; + u64 MemoryRead(u32 vaddr, size_t size) override; std::optional MemoryReadCode(u32 vaddr) override; void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - void MemoryWrite8(u32 vaddr, u8 value) override; - void MemoryWrite16(u32 vaddr, u16 value) override; - void MemoryWrite32(u32 vaddr, u32 value) override; - void MemoryWrite64(u32 vaddr, u64 value) override; + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override; bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override; bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override; bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 0b7f6874a7..894857a86d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -10,6 +10,7 @@ #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/core_timing.h" #include "core/hle/kernel/k_process.h" +#include "dynarmic/interface/A64/config.h" namespace Core { @@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} -u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) { - CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); - return m_memory.Read8(vaddr); -} -u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) { - CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); - return m_memory.Read16(vaddr); -} -u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) { - CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); - return m_memory.Read32(vaddr); -} -u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) { - CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); - return m_memory.Read64(vaddr); +u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) { + CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); + switch (size) { + case sizeof(u64): return m_memory.Read64(vaddr); + case sizeof(u32): return m_memory.Read32(vaddr); + case sizeof(u16): return m_memory.Read16(vaddr); + case sizeof(u8): return m_memory.Read8(vaddr); + default: UNREACHABLE(); + } } Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) { CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); @@ -53,24 +48,15 @@ std::optional DynarmicCallbacks64::MemoryReadCode(u64 vaddr) { return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; } -void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) { - if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { - m_memory.Write8(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) { - if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { - m_memory.Write16(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) { - if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { - m_memory.Write32(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) { - if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { - m_memory.Write64(vaddr, value); +void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) { + if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { + switch (size) { + case sizeof(u64): return m_memory.Write64(vaddr, u64(value)); + case sizeof(u32): return m_memory.Write32(vaddr, u32(value)); + case sizeof(u16): return m_memory.Write16(vaddr, u16(value)); + case sizeof(u8): return m_memory.Write8(vaddr, u8(value)); + default: UNREACHABLE(); + } } } void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) { @@ -211,13 +197,12 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s // Memory if (page_table) { - constexpr size_t PageLog2Stride = 5; - static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData)); - - config.page_table = reinterpret_cast(page_table->entries.data()); + // Dynarmic will not write to the page table, const_cast is safe here + config.page_table = reinterpret_cast( + const_cast(page_table->entries.data())); config.page_table_address_space_bits = std::uint32_t(address_space_bits); - config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; - config.page_table_log2_stride = PageLog2Stride; + config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; + config.page_table_marked_bit = uint8_t(0); config.silently_mirror_page_table = false; config.absolute_offset_page_table = true; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; @@ -231,6 +216,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; config.recompile_on_exclusive_fastmem_failure = true; + + if (reinterpret_cast(m_system.DeviceMemory().buffer.BackingBasePointer() + + Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { + // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, + // we have to manually sign extend when our actual pointer is negative. + config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); + } } // Multi-process state @@ -447,6 +439,7 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) { } void ArmDynarmic64::ClearInstructionCache() { + m_cb->last_code_addr = u64(-1); m_jit->ClearCache(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 4c8570913f..5ea5dde8a6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -16,6 +16,7 @@ #include "common/hash.h" #include "core/arm/arm_interface.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" +#include "dynarmic/interface/A64/config.h" namespace Core::Memory { class Memory; @@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process); - u8 MemoryRead8(u64 vaddr) override; - u16 MemoryRead16(u64 vaddr) override; - u32 MemoryRead32(u64 vaddr) override; - u64 MemoryRead64(u64 vaddr) override; + u64 MemoryRead(u64 vaddr, size_t size) override; Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override; std::optional MemoryReadCode(u64 vaddr) override; void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - void MemoryWrite8(u64 vaddr, u8 value) override; - void MemoryWrite16(u64 vaddr, u16 value) override; - void MemoryWrite32(u64 vaddr, u32 value) override; - void MemoryWrite64(u64 vaddr, u64 value) override; + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override; void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override; bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override; bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override; diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 63d5638fce..438dd4d981 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -369,8 +369,8 @@ void ArmNce::SetContext(const Kernel::Svc::ThreadContext& ctx) { } void ArmNce::SignalInterrupt(Kernel::KThread* thread) { - // Add break loop condition. - m_guest_ctx.esr_el1.fetch_or(static_cast(HaltReason::BreakLoop)); + constexpr u64 break_loop = static_cast(HaltReason::BreakLoop); + if ((m_guest_ctx.esr_el1.fetch_or(break_loop) & break_loop) != 0) return; auto* params = &thread->GetNativeExecutionParameters(); LockThreadParameters(params); diff --git a/src/core/arm/nce/arm_nce_asm_definitions.h b/src/core/arm/nce/arm_nce_asm_definitions.h index 8ea4383f73..ec6abd62d2 100644 --- a/src/core/arm/nce/arm_nce_asm_definitions.h +++ b/src/core/arm/nce/arm_nce_asm_definitions.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -5,9 +8,22 @@ #define __ASSEMBLY__ +// Newer Linux UAPI headers check __ASSEMBLER__ instead of __ASSEMBLY__ to hide C declarations. +// Without it, asm-generic/signal.h redeclares sigset_t and clashes with glibc's. Only define it +// around these includes, since glibc headers also look at __ASSEMBLER__. +#ifndef __ASSEMBLER__ +#define __ASSEMBLER__ +#define EDEN_NCE_UNDEF_ASSEMBLER +#endif + #include #include +#ifdef EDEN_NCE_UNDEF_ASSEMBLER +#undef __ASSEMBLER__ +#undef EDEN_NCE_UNDEF_ASSEMBLER +#endif + #define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2 #define BreakFromRunCodeSignal SIGURG #define GuestAccessFaultSignal SIGSEGV diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp index 11b759edaf..2ccc176323 100644 --- a/src/core/arm/nce/interpreter_visitor.cpp +++ b/src/core/arm/nce/interpreter_visitor.cpp @@ -5,387 +5,779 @@ // SPDX-FileCopyrightText: Copyright 2023 merryhime // SPDX-License-Identifier: GPL-2.0-or-later -#include - +#include #include "core/arm/nce/interpreter_visitor.h" namespace Core { namespace { - -u64 SignExtend(u64 value, size_t bitsize, size_t regsize) { - const u64 mask = u64{1} << (bitsize - 1); - const u64 extended = ((value & ((u64{1} << bitsize) - 1)) ^ mask) - mask; - if (regsize == 64) { - return extended; - } - return static_cast(extended); -} - -u128 VectorGetElement(u128 value, size_t bitsize) { - if (bitsize >= 128) { - return value; - } - if (bitsize >= 64) { - return {value[0], 0}; - } - return {value[0] & ((u64{1} << bitsize) - 1), 0}; -} - +// Prefetch tuning parameters +[[maybe_unused]] constexpr size_t CACHE_LINE_SIZE = 64; +[[maybe_unused]] constexpr size_t PREFETCH_STRIDE = 128; // 2 cache lines ahead +[[maybe_unused]] constexpr size_t SIMD_PREFETCH_THRESHOLD = 32; // Bytes } // namespace -u64 InterpreterVisitor::ExtendReg(Reg reg, Imm<3> option, u8 shift) { - const size_t len = size_t{8} << option.Bits<0, 1, size_t>(); - u64 val = this->GetReg(reg); - if (len < 64) { - val &= (u64{1} << len) - 1; - if (option.Bit<2>()) { - val = SignExtend(val, len, 64); - } - } - return val << shift; +template +u64 SignExtendToLong(u64 value) { + u64 mask = 1ULL << (BitSize - 1); + value &= (1ULL << BitSize) - 1; + return (value ^ mask) - mask; } -bool InterpreterVisitor::Ordered(size_t size, bool load, Reg Rn, Reg Rt) { - const size_t dbytes = size_t{1} << size; - const u64 address = this->GetRegSp(Rn); - if (load) { +static u64 SignExtendToLong(u64 value, u64 bitsize) { + switch (bitsize) { + case 8: + return SignExtendToLong<8>(value); + case 16: + return SignExtendToLong<16>(value); + case 32: + return SignExtendToLong<32>(value); + default: + return value; + } +} + +template +u32 SignExtendToWord(u32 value) { + u32 mask = 1ULL << (BitSize - 1); + value &= (1ULL << BitSize) - 1; + return (value ^ mask) - mask; +} + +static u32 SignExtendToWord(u32 value, u64 bitsize) { + switch (bitsize) { + case 8: + return SignExtendToWord<8>(value); + case 16: + return SignExtendToWord<16>(value); + default: + return value; + } +} + +static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) { + if (regsize == 64) { + return SignExtendToLong(value, bitsize); + } else { + return SignExtendToWord(static_cast(value), bitsize); + } +} + +static u128 VectorGetElement(u128 value, u64 bitsize) { + switch (bitsize) { + case 8: + return {value[0] & ((1ULL << 8) - 1), 0}; + case 16: + return {value[0] & ((1ULL << 16) - 1), 0}; + case 32: + return {value[0] & ((1ULL << 32) - 1), 0}; + case 64: + return {value[0], 0}; + default: + return value; + } +} + +u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) { + ASSERT(shift <= 4); + ASSERT(bitsize == 32 || bitsize == 64); + u64 val = this->GetReg(reg); + size_t len; + u64 extended; + bool signed_extend; + + switch (option.ZeroExtend()) { + case 0b000: { // UXTB + val &= ((1ULL << 8) - 1); + len = 8; + signed_extend = false; + break; + } + case 0b001: { // UXTH + val &= ((1ULL << 16) - 1); + len = 16; + signed_extend = false; + break; + } + case 0b010: { // UXTW + val &= ((1ULL << 32) - 1); + len = 32; + signed_extend = false; + break; + } + case 0b011: { // UXTX + len = 64; + signed_extend = false; + break; + } + case 0b100: { // SXTB + val &= ((1ULL << 8) - 1); + len = 8; + signed_extend = true; + break; + } + case 0b101: { // SXTH + val &= ((1ULL << 16) - 1); + len = 16; + signed_extend = true; + break; + } + case 0b110: { // SXTW + val &= ((1ULL << 32) - 1); + len = 32; + signed_extend = true; + break; + } + case 0b111: { // SXTX + len = 64; + signed_extend = true; + break; + } + default: + UNREACHABLE(); + } + + if (len < bitsize && signed_extend) { + extended = SignExtend(val, len, bitsize); + } else { + extended = val; + } + + return extended << shift; +} + +u128 InterpreterVisitor::GetVec(Vec v) { + return m_fpsimd_regs[static_cast(v)]; +} + +u64 InterpreterVisitor::GetReg(Reg r) { + return m_regs[static_cast(r)]; +} + +u64 InterpreterVisitor::GetSp() { + return m_sp; +} + +u64 InterpreterVisitor::GetPc() { + return m_pc; +} + +void InterpreterVisitor::SetVec(Vec v, u128 value) { + m_fpsimd_regs[static_cast(v)] = value; +} + +void InterpreterVisitor::SetReg(Reg r, u64 value) { + m_regs[static_cast(r)] = value; +} + +void InterpreterVisitor::SetSp(u64 value) { + m_sp = value; +} + +bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) { + const auto memop = L ? MemOp::Load : MemOp::Store; + const size_t elsize = 8 << size; + const size_t datasize = elsize; + const size_t dbytes = datasize / 8; + + u64 address = (Rn == Reg::SP) ? this->GetSp() : this->GetReg(Rn); + switch (memop) { + case MemOp::Store: { + std::atomic_thread_fence(std::memory_order_seq_cst); + u64 value = this->GetReg(Rt); + m_memory.WriteBlock(address, &value, dbytes); + std::atomic_thread_fence(std::memory_order_seq_cst); + break; + } + case MemOp::Load: { u64 value = 0; m_memory.ReadBlock(address, &value, dbytes); this->SetReg(Rt, value); std::atomic_thread_fence(std::memory_order_seq_cst); - return true; + break; + } + default: + UNREACHABLE(); } - std::atomic_thread_fence(std::memory_order_seq_cst); - u64 value = this->GetReg(Rt); - m_memory.WriteBlock(address, &value, dbytes); - std::atomic_thread_fence(std::memory_order_seq_cst); return true; } -bool InterpreterVisitor::LoadLiteral(bool wide, Imm<19> imm19, Reg Rt) { - size_t size = 4; - if (wide) { - size = 8; - } +bool InterpreterVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) { + const size_t size = sz.ZeroExtend(); + const bool L = 0; + const bool o0 = 0; + return this->Ordered(size, L, o0, Rn, Rt); +} + +bool InterpreterVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) { + const size_t size = sz.ZeroExtend(); + const bool L = 0; + const bool o0 = 1; + return this->Ordered(size, L, o0, Rn, Rt); +} + +bool InterpreterVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) { + const size_t size = sz.ZeroExtend(); + const bool L = 1; + const bool o0 = 0; + return this->Ordered(size, L, o0, Rn, Rt); +} + +bool InterpreterVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) { + const size_t size = sz.ZeroExtend(); + const bool L = 1; + const bool o0 = 1; + return this->Ordered(size, L, o0, Rn, Rt); +} + +bool InterpreterVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) { + const size_t size = opc_0 == 0 ? 4 : 8; + const s64 offset = Dynarmic::concatenate(imm19, Imm<2>{0}).SignExtend(); + const u64 address = this->GetPc() + offset; + u64 data = 0; - m_memory.ReadBlock(m_pc + (imm19.SignExtend() << 2), &data, size); + m_memory.ReadBlock(address, &data, size); + this->SetReg(Rt, data); return true; } -bool InterpreterVisitor::LoadLiteralSimd(Imm<2> opc, Imm<19> imm19, Vec Vt) { +bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) { if (opc == 0b11) { + // Unallocated encoding return false; } + + // Size in bytes + const u64 size = 4 << opc.ZeroExtend(); + const u64 offset = imm19.SignExtend() << 2; + const u64 address = this->GetPc() + offset; + u128 data{}; - m_memory.ReadBlock(m_pc + (imm19.SignExtend() << 2), &data, - size_t{4} << opc.ZeroExtend()); + m_memory.ReadBlock(address, &data, size); this->SetVec(Vt, data); return true; } -bool InterpreterVisitor::Pair(Imm<2> opc, bool not_postindex, bool wback, bool load, Imm<7> imm7, - Reg Rt2, Reg Rn, Reg Rt) { - if (!not_postindex && !wback) { - return false; - } - const bool signed_ = opc.Bit<0>(); - if (opc == 0b11 || (!load && signed_)) { - return false; - } - if (load && (Rt == Rt2 || (wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31))) { - return false; - } - if (!load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) { +bool InterpreterVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, + Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) { + if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) { + // Unallocated encoding return false; } + const auto memop = L == 1 ? MemOp::Load : MemOp::Store; + if (memop == MemOp::Load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) { + // Unpredictable instruction + return false; + } + if (memop == MemOp::Store && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) { + // Unpredictable instruction + return false; + } + if (memop == MemOp::Load && Rt == Rt2) { + // Unpredictable instruction + return false; + } + + u64 address; + if (Rn == Reg::SP) { + address = this->GetSp(); + } else { + address = this->GetReg(Rn); + } + + const bool postindex = !not_postindex; + const bool signed_ = opc.Bit<0>() != 0; const size_t scale = 2 + opc.Bit<1>(); - const size_t dbytes = size_t{1} << scale; + const size_t datasize = 8 << scale; const u64 offset = imm7.SignExtend() << scale; - u64 address = this->GetRegSp(Rn); - if (not_postindex) { + + if (!postindex) { address += offset; } - if (load) { - u64 data1 = 0, data2 = 0; - m_memory.ReadBlock(address, &data1, dbytes); - m_memory.ReadBlock(address + dbytes, &data2, dbytes); - if (signed_) { - data1 = SignExtend(data1, dbytes * 8, 64); - data2 = SignExtend(data2, dbytes * 8, 64); - } - this->SetReg(Rt, data1); - this->SetReg(Rt2, data2); - } else { + + const size_t dbytes = datasize / 8; + switch (memop) { + case MemOp::Store: { u64 data1 = this->GetReg(Rt); u64 data2 = this->GetReg(Rt2); m_memory.WriteBlock(address, &data1, dbytes); m_memory.WriteBlock(address + dbytes, &data2, dbytes); + break; } + case MemOp::Load: { + u64 data1 = 0, data2 = 0; + m_memory.ReadBlock(address, &data1, dbytes); + m_memory.ReadBlock(address + dbytes, &data2, dbytes); + if (signed_) { + this->SetReg(Rt, SignExtend(data1, datasize, 64)); + this->SetReg(Rt2, SignExtend(data2, datasize, 64)); + } else { + this->SetReg(Rt, data1); + this->SetReg(Rt2, data2); + } + break; + } + default: + UNREACHABLE(); + } + if (wback) { - if (!not_postindex) { + if (postindex) { address += offset; } - this->SetRegSp(Rn, address); + + if (Rn == Reg::SP) { + this->SetSp(address); + } else { + this->SetReg(Rn, address); + } } + return true; } -bool InterpreterVisitor::PairSimd(Imm<2> opc, bool not_postindex, bool wback, bool load, - Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) { - if (!not_postindex && !wback) { - return false; - } - if (opc == 0b11 || (load && Vt == Vt2)) { +bool InterpreterVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, + Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) { + if (opc == 0b11) { + // Unallocated encoding return false; } + const auto memop = L == 1 ? MemOp::Load : MemOp::Store; + if (memop == MemOp::Load && Vt == Vt2) { + // Unpredictable instruction + return false; + } + + u64 address; + if (Rn == Reg::SP) { + address = this->GetSp(); + } else { + address = this->GetReg(Rn); + } + + const bool postindex = !not_postindex; const size_t scale = 2 + opc.ZeroExtend(); - const size_t dbytes = size_t{1} << scale; + const size_t datasize = 8 << scale; const u64 offset = imm7.SignExtend() << scale; - u64 address = this->GetRegSp(Rn); - if (not_postindex) { + const size_t dbytes = datasize / 8; + + if (!postindex) { address += offset; } - if (load) { + + switch (memop) { + case MemOp::Store: { + u128 data1 = VectorGetElement(this->GetVec(Vt), datasize); + u128 data2 = VectorGetElement(this->GetVec(Vt2), datasize); + m_memory.WriteBlock(address, &data1, dbytes); + m_memory.WriteBlock(address + dbytes, &data2, dbytes); + break; + } + case MemOp::Load: { u128 data1{}, data2{}; m_memory.ReadBlock(address, &data1, dbytes); m_memory.ReadBlock(address + dbytes, &data2, dbytes); this->SetVec(Vt, data1); this->SetVec(Vt2, data2); - } else { - u128 data1 = VectorGetElement(this->GetVec(Vt), dbytes * 8); - u128 data2 = VectorGetElement(this->GetVec(Vt2), dbytes * 8); - m_memory.WriteBlock(address, &data1, dbytes); - m_memory.WriteBlock(address + dbytes, &data2, dbytes); + break; } + default: + UNREACHABLE(); + } + if (wback) { - if (!not_postindex) { + if (postindex) { address += offset; } - this->SetRegSp(Rn, address); + + if (Rn == Reg::SP) { + this->SetSp(address); + } else { + this->SetReg(Rn, address); + } } + return true; } -bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, u64 offset, Imm<2> size, - Imm<2> opc, Reg Rn, Reg Rt) { - const bool signed_ = opc.Bit<1>(); - if (signed_ && (size == 0b11 || (size == 0b10 && opc.Bit<0>()))) { - return false; +bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, + Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) { + MemOp memop; + bool signed_ = false; + size_t regsize = 0; + const size_t datasize = 8 << scale; + + if (opc.Bit<1>() == 0) { + memop = opc.Bit<0>() ? MemOp::Load : MemOp::Store; + regsize = size == 0b11 ? 64 : 32; + } else if (size == 0b11) { + memop = MemOp::Prefetch; + ASSERT(!opc.Bit<0>()); + } else { + memop = MemOp::Load; + ASSERT(!(size == 0b10 && opc.Bit<0>() == 1)); + regsize = opc.Bit<0>() ? 32 : 64; + signed_ = true; } - const size_t datasize = size_t{8} << size.ZeroExtend(); - size_t regsize = 32; - if (size == 0b11 || (signed_ && !opc.Bit<0>())) { - regsize = 64; - } - u64 address = this->GetRegSp(Rn); - if (!postindex) { + u64 address = (Rn == Reg::SP) ? this->GetSp() : this->GetReg(Rn); + if (!postindex) address += offset; + + switch (memop) { + case MemOp::Store: { + u64 data = this->GetReg(Rt); + m_memory.WriteBlock(address, &data, datasize / 8); + break; } - if (signed_ || opc.Bit<0>()) { + case MemOp::Load: { u64 data = 0; m_memory.ReadBlock(address, &data, datasize / 8); if (signed_) { - data = SignExtend(data, datasize, regsize); + this->SetReg(Rt, SignExtend(data, datasize, regsize)); + } else { + this->SetReg(Rt, data); + } + break; + } + case MemOp::Prefetch: + break; + } + + if (wback) { + if (postindex) + address += offset; + if (Rn == Reg::SP) + this->SetSp(address); + else + this->SetReg(Rn, address); + } + return true; +} + +bool InterpreterVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, + Reg Rn, Reg Rt) { + const bool wback = true; + const bool postindex = !not_postindex; + const size_t scale = size.ZeroExtend(); + const u64 offset = imm9.SignExtend(); + + return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt); +} + +bool InterpreterVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) { + const bool wback = false; + const bool postindex = false; + const size_t scale = size.ZeroExtend(); + const u64 offset = imm12.ZeroExtend() << scale; + + return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt); +} + +bool InterpreterVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + const bool wback = false; + const bool postindex = false; + const size_t scale = size.ZeroExtend(); + const u64 offset = imm9.SignExtend(); + + return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt); +} + +bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, + MemOp memop, Reg Rn, Vec Vt) { + const size_t datasize = 8 << scale; + u64 address = (Rn == Reg::SP) ? this->GetSp() : this->GetReg(Rn); + if (!postindex) + address += offset; + + switch (memop) { + case MemOp::Store: { + u128 data = VectorGetElement(this->GetVec(Vt), datasize); + m_memory.WriteBlock(address, &data, datasize / 8); + break; + } + case MemOp::Load: { + u128 data{}; + m_memory.ReadBlock(address, &data, datasize / 8); + this->SetVec(Vt, data); + break; + } + default: + UNREACHABLE(); + } + + if (wback) { + if (postindex) + address += offset; + if (Rn == Reg::SP) + this->SetSp(address); + else + this->SetReg(Rn, address); + } + return true; +} + +bool InterpreterVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, + bool not_postindex, Reg Rn, Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = true; + const bool postindex = !not_postindex; + const u64 offset = imm9.SignExtend(); + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt); +} + +bool InterpreterVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, + Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = false; + const bool postindex = false; + const u64 offset = imm12.ZeroExtend() << scale; + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt); +} + +bool InterpreterVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, + bool not_postindex, Reg Rn, Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = true; + const bool postindex = !not_postindex; + const u64 offset = imm9.SignExtend(); + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt); +} + +bool InterpreterVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, + Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = false; + const bool postindex = false; + const u64 offset = imm12.ZeroExtend() << scale; + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt); +} + +bool InterpreterVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = false; + const bool postindex = false; + const u64 offset = imm9.SignExtend(); + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt); +} + +bool InterpreterVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; + } + + const bool wback = false; + const bool postindex = false; + const u64 offset = imm9.SignExtend(); + + return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt); +} + +bool InterpreterVisitor::RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, + Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) { + MemOp memop; + size_t regsize = 64; + bool signed_ = false; + + if (opc_1 == 0) { + memop = opc_0 == 1 ? MemOp::Load : MemOp::Store; + regsize = size == 0b11 ? 64 : 32; + signed_ = false; + } else if (size == 0b11) { + memop = MemOp::Prefetch; + if (opc_0 == 1) { + // Unallocated encoding + return false; } - this->SetReg(Rt, data); } else { + memop = MemOp::Load; + if (size == 0b10 && opc_0 == 1) { + // Unallocated encoding + return false; + } + regsize = opc_0 == 1 ? 32 : 64; + signed_ = true; + } + + const size_t datasize = 8 << scale; + + // Operation + const u64 offset = this->ExtendReg(64, Rm, option, shift); + + u64 address; + if (Rn == Reg::SP) { + address = this->GetSp(); + } else { + address = this->GetReg(Rn); + } + address += offset; + + switch (memop) { + case MemOp::Store: { u64 data = this->GetReg(Rt); m_memory.WriteBlock(address, &data, datasize / 8); + break; } - if (wback) { - if (postindex) { - address += offset; - } - this->SetRegSp(Rn, address); - } - return true; -} - -bool InterpreterVisitor::RegisterOffset(bool S, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, - Imm<3> option, Reg Rn, Reg Rt) { - if (!option.Bit<1>()) { - return false; - } - const bool high = opc_1 == 1; - const bool wide = size == 0b11; - if (high && opc_0 == 1 && (wide || size == 0b10)) { - return false; - } - if (high && wide) { - return true; - } - - const size_t scale = size.ZeroExtend(); - const size_t datasize = size_t{8} << scale; - size_t regsize = 32; - if (wide || (high && opc_0 == 0)) { - regsize = 64; - } - u8 shift = 0; - if (S) { - shift = static_cast(scale); - } - const u64 address = this->GetRegSp(Rn) + this->ExtendReg(Rm, option, shift); - if (high || opc_0 == 1) { + case MemOp::Load: { u64 data = 0; m_memory.ReadBlock(address, &data, datasize / 8); - if (high) { - data = SignExtend(data, datasize, regsize); + if (signed_) { + this->SetReg(Rt, SignExtend(data, datasize, regsize)); + } else { + this->SetReg(Rt, data); } - this->SetReg(Rt, data); - return true; + break; } - u64 data = this->GetReg(Rt); - m_memory.WriteBlock(address, &data, datasize / 8); + case MemOp::Prefetch: + break; + } + return true; } -bool InterpreterVisitor::SimdImmediate(bool wback, bool postindex, size_t scale, u64 offset, - bool load, Reg Rn, Vec Vt) { - if (scale > 4) { +bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) { + const Imm<1> opc_0{0}; + const size_t scale = size.ZeroExtend(); + const u8 shift = S ? static_cast(scale) : 0; + if (!option.Bit<1>()) { + // Unallocated encoding return false; } + return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt); +} - const size_t dbytes = size_t{1} << scale; - u64 address = this->GetRegSp(Rn); - if (!postindex) { - address += offset; +bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) { + const Imm<1> opc_0{1}; + const size_t scale = size.ZeroExtend(); + const u8 shift = S ? static_cast(scale) : 0; + if (!option.Bit<1>()) { + // Unallocated encoding + return false; } - if (load) { - u128 data{}; - m_memory.ReadBlock(address, &data, dbytes); - this->SetVec(Vt, data); + return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt); +} + +bool InterpreterVisitor::SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, + Reg Rn, Vec Vt) { + const auto memop = opc_0 == 1 ? MemOp::Load : MemOp::Store; + const size_t datasize = 8 << scale; + + // Operation + const u64 offset = this->ExtendReg(64, Rm, option, shift); + + u64 address; + if (Rn == Reg::SP) { + address = this->GetSp(); } else { - u128 data = VectorGetElement(this->GetVec(Vt), dbytes * 8); - m_memory.WriteBlock(address, &data, dbytes); + address = this->GetReg(Rn); } - if (wback) { - if (postindex) { - address += offset; - } - this->SetRegSp(Rn, address); + address += offset; + + switch (memop) { + case MemOp::Store: { + u128 data = VectorGetElement(this->GetVec(Vt), datasize); + m_memory.WriteBlock(address, &data, datasize / 8); + break; } + case MemOp::Load: { + u128 data{}; + m_memory.ReadBlock(address, &data, datasize / 8); + this->SetVec(Vt, data); + break; + } + default: + UNREACHABLE(); + } + return true; } -bool InterpreterVisitor::SimdOffset(size_t scale, bool S, bool load, Reg Rm, Imm<3> option, Reg Rn, - Vec Vt) { - if (scale > 4 || !option.Bit<1>()) { +bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, + Reg Rn, Vec Vt) { + const Imm<1> opc_0{0}; + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding return false; } - - const size_t dbytes = size_t{1} << scale; - u8 shift = 0; - if (S) { - shift = static_cast(scale); + const u8 shift = S ? static_cast(scale) : 0; + if (!option.Bit<1>()) { + // Unallocated encoding + return false; } - const u64 address = this->GetRegSp(Rn) + this->ExtendReg(Rm, option, shift); - if (load) { - u128 data{}; - m_memory.ReadBlock(address, &data, dbytes); - this->SetVec(Vt, data); - return true; - } - u128 data = VectorGetElement(this->GetVec(Vt), dbytes * 8); - m_memory.WriteBlock(address, &data, dbytes); - return true; + return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt); } -bool InterpreterVisitor::Execute(u32 inst) { - const Imm<2> size{inst >> 30}; - const Imm<2> opc{(inst >> 22) & 3}; - const Imm<3> option{(inst >> 13) & 7}; - const Imm<19> imm19{(inst >> 5) & 0x7FFFF}; - const Reg Rn = static_cast((inst >> 5) & 31); - const Reg Rt = static_cast(inst & 31); - const Vec Vt = static_cast(inst & 31); - const bool load = (inst & (1U << 22)) != 0; - const bool not_postindex = (inst & (1U << 11)) != 0; - const bool scaled = (inst & (1U << 12)) != 0; - const size_t vscale = ((inst >> 21) & 4) | (inst >> 30); - const u64 imm9 = Imm<9>{(inst >> 12) & 0x1FF}.SignExtend(); - - switch ((inst >> 24) & 0x3F) { - case 0b001000: - if ((inst & 0x00BF7C00) != 0x009F7C00) { - return false; - } - return this->Ordered(size.ZeroExtend(), load, Rn, Rt); - case 0b011000: - if ((inst & 0x80000000) != 0) { - return false; - } - return this->LoadLiteral((inst & 0x40000000) != 0, imm19, Rt); - case 0b011100: - return this->LoadLiteralSimd(size, imm19, Vt); - case 0b101000: - case 0b101001: - return this->Pair(size, (inst & (1U << 24)) != 0, (inst & (1U << 23)) != 0, load, - Imm<7>{(inst >> 15) & 0x7F}, static_cast((inst >> 10) & 31), Rn, Rt); - case 0b101100: - case 0b101101: - return this->PairSimd(size, (inst & (1U << 24)) != 0, (inst & (1U << 23)) != 0, load, - Imm<7>{(inst >> 15) & 0x7F}, static_cast((inst >> 10) & 31), Rn, - Vt); - case 0b111000: - if ((inst & (1U << 21)) != 0) { - if ((inst & 0x00000C00) != 0x00000800) { - return false; - } - return this->RegisterOffset(scaled, size, Imm<1>{(inst >> 23) & 1}, - Imm<1>{(inst >> 22) & 1}, - static_cast((inst >> 16) & 31), option, Rn, Rt); - } - if ((inst & (1U << 10)) != 0) { - return this->RegisterImmediate(true, !not_postindex, imm9, size, opc, Rn, Rt); - } - if (not_postindex) { - return false; - } - return this->RegisterImmediate(false, false, imm9, size, opc, Rn, Rt); - case 0b111001: - return this->RegisterImmediate(false, false, - u64{(inst >> 10) & 0xFFF} << size.ZeroExtend(), size, - opc, Rn, Rt); - case 0b111100: - if ((inst & (1U << 21)) != 0) { - if ((inst & 0x00000C00) != 0x00000800) { - return false; - } - return this->SimdOffset(vscale, scaled, load, static_cast((inst >> 16) & 31), - option, Rn, Vt); - } - if ((inst & (1U << 10)) != 0) { - return this->SimdImmediate(true, !not_postindex, vscale, imm9, load, Rn, Vt); - } - if (not_postindex) { - return false; - } - return this->SimdImmediate(false, false, vscale, imm9, load, Rn, Vt); - case 0b111101: - return this->SimdImmediate(false, false, vscale, u64{(inst >> 10) & 0xFFF} << vscale, load, - Rn, Vt); +bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, + Reg Rn, Vec Vt) { + const Imm<1> opc_0{1}; + const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend(); + if (scale > 4) { + // Unallocated encoding + return false; } - return false; + const u8 shift = S ? static_cast(scale) : 0; + if (!option.Bit<1>()) { + // Unallocated encoding + return false; + } + return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt); } -std::optional MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, - fpsimd_context* fpsimd_context) { +std::optional MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, fpsimd_context* fpsimd_context) { std::span regs(reinterpret_cast(context->regs), 31); std::span vregs(reinterpret_cast(fpsimd_context->vregs), 32); u64& sp = *reinterpret_cast(&context->sp); const u64& pc = *reinterpret_cast(&context->pc); InterpreterVisitor visitor(memory, regs, vregs, sp, pc); - if (!visitor.Execute(memory.Read32(pc))) { - return std::nullopt; + u32 instruction = memory.Read32(pc); + bool was_executed = false; + + auto decoder = Dynarmic::A64::Decode(visitor, instruction); + if (decoder) { + was_executed = *decoder; + } else { + was_executed = false; } - return pc + 4; + return was_executed ? std::optional(pc + 4) : std::nullopt; } } // namespace Core diff --git a/src/core/arm/nce/interpreter_visitor.h b/src/core/arm/nce/interpreter_visitor.h index 667ea24ac8..131b69cd7d 100644 --- a/src/core/arm/nce/interpreter_visitor.h +++ b/src/core/arm/nce/interpreter_visitor.h @@ -7,77 +7,97 @@ #pragma once -#include -#include +#include #include +#include +#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#include -#include -#pragma GCC diagnostic pop - +#include "core/hle/kernel/k_thread.h" #include "core/memory.h" +#include "common/logging.h" +#include "core/arm/nce/visitor_base.h" namespace Core { -class InterpreterVisitor { +namespace Memory { +class Memory; +} + +class InterpreterVisitor final : public VisitorBase { public: explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span regs, std::span fpsimd_regs, u64& sp, const u64& pc) : m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {} + ~InterpreterVisitor() override = default; - bool Execute(u32 inst); + enum class MemOp { + Load, + Store, + Prefetch, + }; + + u128 GetVec(Vec v); + u64 GetReg(Reg r); + u64 GetSp(); + u64 GetPc(); + + void SetVec(Vec v, u128 value); + void SetReg(Reg r, u64 value); + void SetSp(u64 value); + + u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift); + + // Loads and stores - Load/Store Exclusive + bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt); + bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override; + bool STLR(Imm<2> size, Reg Rn, Reg Rt) override; + bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override; + bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override; + + // Loads and stores - Load register (literal) + bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override; + bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override; + + // Loads and stores - Load/Store register pair + bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2, + Reg Rn, Reg Rt) override; + bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, + Reg Rn, Vec Vt) override; + + // Loads and stores - Load/Store register (immediate) + bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size, + Imm<2> opc, Reg Rn, Reg Rt); + bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, + Reg Rt) override; + bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override; + bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override; + + bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn, + Vec Vt); + bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, + Vec Vt) override; + bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override; + bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, + Vec Vt) override; + bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override; + bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override; + bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override; + + // Loads and stores - Load/Store register (register offset) + bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, + Imm<3> option, Reg Rn, Reg Rt); + bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) override; + bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) override; + + bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt); + bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Vec Vt) override; + bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Vec Vt) override; private: - template - using Imm = Dynarmic::Imm; - using Reg = Dynarmic::A64::Reg; - using Vec = Dynarmic::A64::Vec; - - u128 GetVec(Vec v) const { - return m_fpsimd_regs[static_cast(v)]; - } - void SetVec(Vec v, u128 value) { - m_fpsimd_regs[static_cast(v)] = value; - } - u64 GetReg(Reg r) const { - return m_regs[static_cast(r)]; - } - void SetReg(Reg r, u64 value) { - m_regs[static_cast(r)] = value; - } - u64 GetRegSp(Reg r) const { - if (r == Reg::SP) { - return m_sp; - } - return m_regs[static_cast(r)]; - } - void SetRegSp(Reg r, u64 value) { - if (r == Reg::SP) { - m_sp = value; - return; - } - m_regs[static_cast(r)] = value; - } - - u64 ExtendReg(Reg reg, Imm<3> option, u8 shift); - - bool Ordered(size_t size, bool load, Reg Rn, Reg Rt); - bool LoadLiteral(bool wide, Imm<19> imm19, Reg Rt); - bool LoadLiteralSimd(Imm<2> opc, Imm<19> imm19, Vec Vt); - bool Pair(Imm<2> opc, bool not_postindex, bool wback, bool load, Imm<7> imm7, Reg Rt2, Reg Rn, - Reg Rt); - bool PairSimd(Imm<2> opc, bool not_postindex, bool wback, bool load, Imm<7> imm7, Vec Vt2, - Reg Rn, Vec Vt); - bool RegisterImmediate(bool wback, bool postindex, u64 offset, Imm<2> size, Imm<2> opc, Reg Rn, - Reg Rt); - bool RegisterOffset(bool S, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, Imm<3> option, - Reg Rn, Reg Rt); - bool SimdImmediate(bool wback, bool postindex, size_t scale, u64 offset, bool load, Reg Rn, - Vec Vt); - bool SimdOffset(size_t scale, bool S, bool load, Reg Rm, Imm<3> option, Reg Rn, Vec Vt); - Core::Memory::Memory& m_memory; std::span m_regs; std::span m_fpsimd_regs; diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index b1569ceee6..f21d7cacdc 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -45,6 +45,23 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) { WriteLoadContext(c_pre); } +Patcher::Patcher(std::span text) : c(m_patch_instructions), c_pre(m_patch_instructions_pre) { + modules.emplace_back(); + curr_patch = &modules.back(); + const auto words = std::span{reinterpret_cast(text.data()), text.size() / sizeof(u32)}; + for (u32 i = ModuleCodeIndex; i < static_cast(words.size()); ++i) { + const uintptr_t offset = i * sizeof(u32); + if (auto mrs = MRS{words[i]}; mrs.Verify() && mrs.GetRt() != 31 && (mrs.GetSystemReg() == TpidrEl0 || mrs.GetSystemReg() == TpidrroEl0)) { + BranchToPatch(offset); + const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0 : oaknut::SystemReg::TPIDR_EL0; + WriteMrsHandler(offset + sizeof(u32), oaknut::XReg{static_cast(mrs.GetRt())}, src_reg); + } else if (auto msr = MSR{words[i]}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) { + BranchToPatch(offset); + WriteMsrHandler(offset + sizeof(u32), oaknut::XReg{static_cast(msr.GetRt())}); + } + } +} + Patcher::~Patcher() = default; bool Patcher::PatchText(std::span program_image, const Kernel::CodeSet::Segment& code) { @@ -182,6 +199,31 @@ bool Patcher::PatchText(std::span program_image, const Kernel::CodeSet return true; } +std::span Patcher::RelocateRuntimeTls( + s64 patch_offset, std::vector& out_patches) { + const auto& patch = modules.front(); + const auto can_branch = [](s64 offset) { + return offset % 4 == 0 && offset >= -static_cast(MaxRelativeBranch) && offset < static_cast(MaxRelativeBranch); + }; + out_patches.clear(); + out_patches.reserve(patch.m_branch_to_patch_relocations.size()); + for (const auto& rel : patch.m_branch_to_patch_relocations) { + const s64 offset = patch_offset + rel.patch_offset - static_cast(rel.module_offset); + if (!can_branch(offset)) {return {};} + u32 replacement; + oaknut::CodeGenerator rc{&replacement}; + rc.B(offset); + out_patches.push_back({rel.module_offset, replacement}); + } + for (const auto& rel : patch.m_branch_to_module_relocations) { + const s64 offset = static_cast(rel.module_offset) - patch_offset - rel.patch_offset; + if (!can_branch(offset)) {return {};} + oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; + rc.B(offset); + } + return m_patch_instructions; +} + bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector& program_image, EntryTrampolines* out_trampolines) { const size_t patch_size = GetSectionSize(); const size_t pre_patch_size = GetPreSectionSize(); @@ -371,9 +413,14 @@ size_t Patcher::GetPreSectionSize() const noexcept { } void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) { + // This function was called, which modifies X30, so use that as a scratch register. + // SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes + // of stack. cg.STR(X30, SP, 8); - cg.LDR(X30, SP, 0); + cg.MRS(X30, oaknut::SystemReg::TPIDR_EL0); + cg.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context)); + // Load system registers. cg.LDR(W0, X30, offsetof(GuestContext, fpsr)); cg.MSR(oaknut::SystemReg::FPSR, X0); cg.LDR(W0, X30, offsetof(GuestContext, fpcr)); @@ -381,9 +428,7 @@ void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) { cg.LDR(W0, X30, offsetof(GuestContext, nzcv)); cg.MSR(oaknut::SystemReg::NZCV, X0); - cg.LDR(X0, X30, 8 * 30); - cg.STR(X0, SP, 0); - + // Load all vector registers. static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers); for (int i = 0; i <= 30; i += 2) { cg.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i); @@ -433,7 +478,7 @@ void Patcher::WriteSaveContext(oaknut::VectorCodeGenerator& cg) { cg.STR(W0, X30, offsetof(GuestContext, nzcv)); cg.LDR(X0, SP, POST_INDEXED, 16); - cg.MOV(X1, X30); + // Reload our return X30 from the stack, and return. cg.LDR(X30, SP, 8); cg.RET(); } @@ -454,6 +499,8 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut // Now that we've saved all registers, we can use any registers as scratch. // Store PC + 4 to arm interface, since we know the instruction offset from the entry point. oaknut::Label pc_after_svc; + cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0); + cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context)); cg.LDR(X2, pc_after_svc); cg.STR(X2, X1, offsetof(GuestContext, pc)); @@ -509,13 +556,24 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut // Host called this location. Save the return address so we can // unwind the stack properly when jumping back. - cg.ADD(X0, X1, offsetof(GuestContext, host_ctx)); + cg.MRS(X2, oaknut::SystemReg::TPIDR_EL0); + cg.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context)); + cg.ADD(X0, X2, offsetof(GuestContext, host_ctx)); cg.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64)); - cg.STR(X1, SP, PRE_INDEXED, -16); + // Reload all guest registers except X30 and PC. + // The function also expects 16 bytes of stack already allocated. + cg.STR(X30, SP, PRE_INDEXED, -16); cg.BL(load_ctx); cg.LDR(X30, SP, POST_INDEXED, 16); + // Use X1 as a scratch register to restore X30. + cg.STR(X1, SP, PRE_INDEXED, -16); + cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0); + cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context)); + cg.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30); + cg.LDR(X1, SP, POST_INDEXED, 16); + // Unlock the context. this->UnlockContext(cg); diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index a3b314000c..695161cd18 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h @@ -48,15 +48,22 @@ using ModuleTextAddress = u64; using PatchTextAddress = u64; using EntryTrampolines = ::Common::unordered_map; +struct InstructionPatch { + u64 offset; + u32 replacement; +}; + class Patcher { public: void SetModuleID(const ModuleID& id) { module_id = id; } explicit Patcher(); + explicit Patcher(std::span text); ~Patcher(); bool PatchText(std::span program_image, const Kernel::CodeSet::Segment& code); bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector& program_image, EntryTrampolines* out_trampolines); + std::span RelocateRuntimeTls(s64 patch_offset, std::vector& out_patches); size_t GetSectionSize() const noexcept; size_t GetPreSectionSize() const noexcept; diff --git a/src/core/arm/nce/visitor_base.h b/src/core/arm/nce/visitor_base.h new file mode 100644 index 0000000000..6a2be3d9bc --- /dev/null +++ b/src/core/arm/nce/visitor_base.h @@ -0,0 +1,2783 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 merryhime +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" + +#include +#include +#include + +#pragma GCC diagnostic pop + +namespace Core { + +class VisitorBase { +public: + using instruction_return_type = bool; + + template + using Imm = Dynarmic::Imm; + using Reg = Dynarmic::A64::Reg; + using Vec = Dynarmic::A64::Vec; + using Cond = Dynarmic::A64::Cond; + + virtual ~VisitorBase() {} + + virtual bool UnallocatedEncoding() { + return false; + } + + // Data processing - Immediate - PC relative addressing + virtual bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd) { + return false; + } + virtual bool ADRP(Imm<2> immlo, Imm<19> immhi, Reg Rd) { + return false; + } + + // Data processing - Immediate - Add/Sub (with tag) + virtual bool ADDG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) { + return false; + } + + // Data processing - Immediate - Add/Sub + virtual bool ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + return false; + } + virtual bool ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + return false; + } + + // Data processing - Immediate - Logical + virtual bool AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + + // Data processing - Immediate - Move Wide + virtual bool MOVN(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + return false; + } + virtual bool MOVZ(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + return false; + } + virtual bool MOVK(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + return false; + } + + // Data processing - Immediate - Bitfield + virtual bool SBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool BFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool UBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + virtual bool ASR_1(Imm<5> immr, Reg Rn, Reg Rd) { + return false; + } + virtual bool ASR_2(Imm<6> immr, Reg Rn, Reg Rd) { + return false; + } + virtual bool SXTB_1(Reg Rn, Reg Rd) { + return false; + } + virtual bool SXTB_2(Reg Rn, Reg Rd) { + return false; + } + virtual bool SXTH_1(Reg Rn, Reg Rd) { + return false; + } + virtual bool SXTH_2(Reg Rn, Reg Rd) { + return false; + } + virtual bool SXTW(Reg Rn, Reg Rd) { + return false; + } + + // Data processing - Immediate - Extract + virtual bool EXTR(bool sf, bool N, Reg Rm, Imm<6> imms, Reg Rn, Reg Rd) { + return false; + } + + // Conditional branch + virtual bool B_cond(Imm<19> imm19, Cond cond) { + return false; + } + + // Exception generation + virtual bool SVC(Imm<16> imm16) { + return false; + } + virtual bool HVC(Imm<16> imm16) { + return false; + } + virtual bool SMC(Imm<16> imm16) { + return false; + } + virtual bool BRK(Imm<16> imm16) { + return false; + } + virtual bool HLT(Imm<16> imm16) { + return false; + } + virtual bool DCPS1(Imm<16> imm16) { + return false; + } + virtual bool DCPS2(Imm<16> imm16) { + return false; + } + virtual bool DCPS3(Imm<16> imm16) { + return false; + } + + // System + virtual bool MSR_imm(Imm<3> op1, Imm<4> CRm, Imm<3> op2) { + return false; + } + virtual bool HINT(Imm<4> CRm, Imm<3> op2) { + return false; + } + virtual bool NOP() { + return false; + } + virtual bool YIELD() { + return false; + } + virtual bool WFE() { + return false; + } + virtual bool WFI() { + return false; + } + virtual bool SEV() { + return false; + } + virtual bool SEVL() { + return false; + } + virtual bool XPAC_1(bool D, Reg Rd) { + return false; + } + virtual bool XPAC_2() { + return false; + } + virtual bool PACIA_1(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool PACIA_2() { + return false; + } + virtual bool PACIB_1(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool PACIB_2() { + return false; + } + virtual bool AUTIA_1(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool AUTIA_2() { + return false; + } + virtual bool AUTIB_1(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool AUTIB_2() { + return false; + } + virtual bool BTI(Imm<2> upper_op2) { + return false; + } + virtual bool ESB() { + return false; + } + virtual bool PSB() { + return false; + } + virtual bool TSB() { + return false; + } + virtual bool CSDB() { + return false; + } + virtual bool CLREX(Imm<4> CRm) { + return false; + } + virtual bool DSB(Imm<4> CRm) { + return false; + } + virtual bool SSBB() { + return false; + } + virtual bool PSSBB() { + return false; + } + virtual bool DMB(Imm<4> CRm) { + return false; + } + virtual bool ISB(Imm<4> CRm) { + return false; + } + virtual bool SYS(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + return false; + } + virtual bool SB() { + return false; + } + virtual bool MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + return false; + } + virtual bool SYSL(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + return false; + } + virtual bool MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + return false; + } + + // System - Flag manipulation instructions + virtual bool CFINV() { + return false; + } + virtual bool RMIF(Imm<6> lsb, Reg Rn, Imm<4> mask) { + return false; + } + virtual bool SETF8(Reg Rn) { + return false; + } + virtual bool SETF16(Reg Rn) { + return false; + } + + // System - Flag format instructions + virtual bool XAFlag() { + return false; + } + virtual bool AXFlag() { + return false; + } + + // SYS: Data Cache + virtual bool DC_IVAC(Reg Rt) { + return false; + } + virtual bool DC_ISW(Reg Rt) { + return false; + } + virtual bool DC_CSW(Reg Rt) { + return false; + } + virtual bool DC_CISW(Reg Rt) { + return false; + } + virtual bool DC_ZVA(Reg Rt) { + return false; + } + virtual bool DC_CVAC(Reg Rt) { + return false; + } + virtual bool DC_CVAU(Reg Rt) { + return false; + } + virtual bool DC_CVAP(Reg Rt) { + return false; + } + virtual bool DC_CIVAC(Reg Rt) { + return false; + } + + // SYS: Instruction Cache + virtual bool IC_IALLU() { + return false; + } + virtual bool IC_IALLUIS() { + return false; + } + virtual bool IC_IVAU(Reg Rt) { + return false; + } + + // Unconditional branch (Register) + virtual bool BR(Reg Rn) { + return false; + } + virtual bool BRA(bool Z, bool M, Reg Rn, Reg Rm) { + return false; + } + virtual bool BLR(Reg Rn) { + return false; + } + virtual bool BLRA(bool Z, bool M, Reg Rn, Reg Rm) { + return false; + } + virtual bool RET(Reg Rn) { + return false; + } + virtual bool RETA(bool M) { + return false; + } + virtual bool ERET() { + return false; + } + virtual bool ERETA(bool M) { + return false; + } + virtual bool DRPS() { + return false; + } + + // Unconditional branch (immediate) + virtual bool B_uncond(Imm<26> imm26) { + return false; + } + virtual bool BL(Imm<26> imm26) { + return false; + } + + // Compare and branch (immediate) + virtual bool CBZ(bool sf, Imm<19> imm19, Reg Rt) { + return false; + } + virtual bool CBNZ(bool sf, Imm<19> imm19, Reg Rt) { + return false; + } + virtual bool TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + return false; + } + virtual bool TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + return false; + } + + // Loads and stores - Advanced SIMD Load/Store multiple structures + virtual bool STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool STx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LDx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LDx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + + // Loads and stores - Advanced SIMD Load/Store single structures + virtual bool ST1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool ST1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool ST3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool ST3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool ST2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool ST2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool ST4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool ST4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LD3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LD1R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD1R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD3R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD3R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LD4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LD2R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD2R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD4R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + virtual bool LD4R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + return false; + } + + // Loads and stores - Load/Store Exclusive + virtual bool STXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool STLXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool STXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STLXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDXR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAXR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STLLR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool STLR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAR(Imm<2> size, Reg Rn, Reg Rt) { + return false; + } + virtual bool CASP(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + return false; + } + virtual bool CASB(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + return false; + } + virtual bool CASH(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + return false; + } + virtual bool CAS(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + return false; + } + + // Loads and stores - Load register (literal) + virtual bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) { + return false; + } + virtual bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) { + return false; + } + virtual bool LDRSW_lit(Imm<19> imm19, Reg Rt) { + return false; + } + virtual bool PRFM_lit(Imm<19> imm19, Imm<5> prfop) { + return false; + } + + // Loads and stores - Load/Store no-allocate pair + virtual bool STNP_LDNP_gen(Imm<1> upper_opc, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STNP_LDNP_fpsimd(Imm<2> opc, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) { + return false; + } + + // Loads and stores - Load/Store register pair + virtual bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, + Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, + Vec Vt2, Reg Rn, Vec Vt) { + return false; + } + virtual bool STGP_1(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STGP_2(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + virtual bool STGP_3(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + return false; + } + + // Loads and stores - Load/Store register (immediate) + virtual bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, + Reg Rt) { + return false; + } + virtual bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) { + return false; + } + virtual bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool PRFM_imm(Imm<12> imm12, Reg Rn, Reg Rt) { + return false; + } + virtual bool PRFM_unscaled_imm(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, + Reg Rn, Vec Vt) { + return false; + } + virtual bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) { + return false; + } + virtual bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, + Reg Rn, Vec Vt) { + return false; + } + virtual bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) { + return false; + } + virtual bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + return false; + } + virtual bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + return false; + } + + // Loads and stores - Load/Store register (unprivileged) + virtual bool STTRB(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTRB(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTRSB(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool STTRH(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTRH(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTRSH(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool STTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) { + return false; + } + + // Loads and stores - Atomic memory options + virtual bool LDADDB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDCLRB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDEORB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSETB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool SWPB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAPRB(Reg Rn, Reg Rt) { + return false; + } + virtual bool LDADDH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDCLRH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDEORH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSETH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool SWPH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAPRH(Reg Rn, Reg Rt) { + return false; + } + virtual bool LDADD(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDCLR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDEOR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSET(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDSMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDUMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool SWP(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + return false; + } + virtual bool LDAPR(Reg Rn, Reg Rt) { + return false; + } + + // Loads and stores - Load/Store register (register offset) + virtual bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) { + return false; + } + virtual bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Reg Rt) { + return false; + } + virtual bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Vec Vt) { + return false; + } + virtual bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, + Vec Vt) { + return false; + } + + // Loads and stores - Load/Store memory tags + virtual bool STG_1(Imm<9> imm9, Reg Rn) { + return false; + } + virtual bool STG_2(Imm<9> imm9, Reg Rn) { + return false; + } + virtual bool STG_3(Imm<9> imm9, Reg Rn) { + return false; + } + virtual bool LDG(Imm<9> offset_imm, Reg Rn, Reg Rt) { + return false; + } + virtual bool STZG_1(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool STZG_2(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool STZG_3(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool ST2G_1(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool ST2G_2(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool ST2G_3(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool STGV(Reg Rn, Reg Rt) { + return false; + } + virtual bool STZ2G_1(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool STZ2G_2(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool STZ2G_3(Imm<9> offset_imm, Reg Rn) { + return false; + } + virtual bool LDGV(Reg Rn, Reg Rt) { + return false; + } + + // Loads and stores - Load/Store register (pointer authentication) + virtual bool LDRA(bool M, bool S, Imm<9> imm9, bool W, Reg Rn, Reg Rt) { + return false; + } + + // Data Processing - Register - 2 source + virtual bool UDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool SDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool LSLV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool LSRV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool ASRV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool RORV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool CRC32(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) { + return false; + } + virtual bool CRC32C(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) { + return false; + } + virtual bool PACGA(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBP(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool IRG(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool GMI(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBPS(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - 1 source + virtual bool RBIT_int(bool sf, Reg Rn, Reg Rd) { + return false; + } + virtual bool REV16_int(bool sf, Reg Rn, Reg Rd) { + return false; + } + virtual bool REV(bool sf, bool opc_0, Reg Rn, Reg Rd) { + return false; + } + virtual bool CLZ_int(bool sf, Reg Rn, Reg Rd) { + return false; + } + virtual bool CLS_int(bool sf, Reg Rn, Reg Rd) { + return false; + } + virtual bool REV32_int(Reg Rn, Reg Rd) { + return false; + } + virtual bool PACDA(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool PACDB(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool AUTDA(bool Z, Reg Rn, Reg Rd) { + return false; + } + virtual bool AUTDB(bool Z, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - Logical (shifted register) + virtual bool AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - Add/Sub (shifted register) + virtual bool ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - Add/Sub (shifted register) + virtual bool ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + return false; + } + virtual bool ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + return false; + } + virtual bool SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - Add/Sub (with carry) + virtual bool ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - Conditional compare + virtual bool CCMN_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) { + return false; + } + virtual bool CCMP_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) { + return false; + } + virtual bool CCMN_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) { + return false; + } + virtual bool CCMP_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) { + return false; + } + + // Data Processing - Register - Conditional select + virtual bool CSEL(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + return false; + } + virtual bool CSINC(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + return false; + } + virtual bool CSINV(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + return false; + } + virtual bool CSNEG(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - Register - 3 source + virtual bool MADD(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool MSUB(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool SMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool SMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool SMULH(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + virtual bool UMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool UMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + return false; + } + virtual bool UMULH(Reg Rm, Reg Rn, Reg Rd) { + return false; + } + + // Data Processing - FP and SIMD - AES + virtual bool AESE(Vec Vn, Vec Vd) { + return false; + } + virtual bool AESD(Vec Vn, Vec Vd) { + return false; + } + virtual bool AESMC(Vec Vn, Vec Vd) { + return false; + } + virtual bool AESIMC(Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SHA + virtual bool SHA1C(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA1P(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA1M(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA1SU0(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA256H(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA256H2(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA256SU1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA1H(Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA1SU1(Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA256SU0(Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar copy + virtual bool DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar three + virtual bool FMULX_vec_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMULX_vec_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_reg_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPS_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTS_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_reg_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGE_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGE_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABD_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABD_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_reg_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGT_1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGT_2(bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Two register misc FP16 + virtual bool FCVTNS_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMS_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAS_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_int_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_zero_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_zero_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLT_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPS_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_int_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPE_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPX_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTNU_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMU_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAU_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_int_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_zero_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLE_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPU_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_int_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTE_1(Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Two register misc + virtual bool FCVTNS_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMS_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAS_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_int_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_zero_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_zero_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLT_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPS_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPE_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPX_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTNU_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMU_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAU_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_int_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_zero_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLE_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPU_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_int_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTE_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar two register misc FP16 + virtual bool FCVTNS_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMS_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAS_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_int_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_zero_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_zero_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLT_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPS_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_int_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPE_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTNU_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMU_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAU_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_int_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_zero_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLE_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPU_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_int_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTE_3(bool Q, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar two register misc + virtual bool FCVTNS_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMS_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAS_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLT_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPE_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTNU_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTMU_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTAU_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLE_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTPU_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTE_4(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar three same extra + virtual bool SQRDMLAH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMLAH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMLSH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMLSH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Scalar two-register misc + virtual bool SUQADD_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQABS_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGT_zero_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMEQ_zero_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMLT_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool ABS_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQXTN_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool USQADD_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQNEG_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGE_zero_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMLE_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool NEG_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQXTUN_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQXTN_1(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTXN_1(bool sz, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Scalar pairwise + virtual bool ADDP_pair(Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMP_pair_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMP_pair_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADDP_pair_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FADDP_pair_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXP_pair_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXP_pair_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMP_pair_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMP_pair_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINP_pair_1(Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINP_pair_2(bool sz, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Scalar three different + virtual bool SQDMLAL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + return false; + } + virtual bool SQDMLSL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + return false; + } + virtual bool SQDMULL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Scalar three same + virtual bool SQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGT_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGE_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMTST_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMHI_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMHS_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMEQ_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Scalar shift by immediate + virtual bool SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQRSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Scalar x indexed element + virtual bool SQDMLAL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMLSL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLA_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLA_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLS_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLS_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMUL_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMUL_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMLAH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQRDMLSH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMULX_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMULX_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Table Lookup + virtual bool TBL(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) { + return false; + } + virtual bool TBX(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Permute + virtual bool UZP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool TRN1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ZIP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UZP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool TRN2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ZIP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Extract + virtual bool EXT(bool Q, Vec Vm, Imm<4> imm4, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Copy + virtual bool DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) { + return false; + } + virtual bool DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) { + return false; + } + virtual bool SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { + return false; + } + virtual bool UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { + return false; + } + virtual bool INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) { + return false; + } + virtual bool INS_elt(Imm<5> imm5, Imm<4> imm4, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Three same + virtual bool FMULX_vec_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPS_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTS_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGE_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABD_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGT_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNM_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLA_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADD_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAX_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNM_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLS_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSUB_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMIN_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADDP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMUL_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FDIV_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINP_vec_1(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Three same extra + virtual bool SDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMLA_vec(bool Q, Imm<2> size, Vec Vm, Imm<2> rot, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCADD_vec(bool Q, Imm<2> size, Vec Vm, Imm<1> rot, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Two register misc + virtual bool REV64_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool REV16_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CLS_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CNT(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool XTN(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool URECPE(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool REV32_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CLZ_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHLL(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool NOT(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool RBIT_asimd(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSQRTE(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool SUQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGT_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMEQ_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMLT_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool ABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool USQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQNEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGE_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMLE_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool NEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQXTUN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTN_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTN_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTM_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTM_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABS_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABS_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTP_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTP_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTZ_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTZ_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTA_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTA_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTX_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTX_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNEG_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNEG_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTI_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTI_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSQRT_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSQRT_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT32X_1(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT64X_1(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT32Z_1(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT64Z_1(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD across lanes + virtual bool SADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool ADDV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMV_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXV_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXV_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMV_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINV_1(bool Q, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINV_2(bool Q, bool sz, Vec Vn, Vec Vd) { + return false; + } + virtual bool UADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD three different + virtual bool SADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool PMULL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool USUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool USUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool RADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool RSUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMLAL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMLSL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMULL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD three same + virtual bool SHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool MLA_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool MUL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ADDP_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLAL_vec_1(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLAL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool AND_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool BIC_asimd_reg(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLSL_vec_1(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLSL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ORR_asimd_reg(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ORN_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool URHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool MLS_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool PMUL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool EOR_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool BSL(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool BIT(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool BIF(bool Q, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLA_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADD_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAX_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLS_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSUB_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMIN_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADDP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMUL_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FDIV_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMULX_vec_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMEQ_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRECPS_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRSQRTS_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGE_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGE_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABD_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCMGT_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FACGT_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGT_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMGE_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool ADD_vector(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMTST_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMHI_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMHS_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool CMEQ_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD modified immediate + virtual bool MOVI(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<4> cmode, Imm<1> d, + Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) { + return false; + } + virtual bool FMOV_2(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, + Imm<1> g, Imm<1> h, Vec Vd) { + return false; + } + virtual bool FMOV_3(bool Q, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, + Imm<1> g, Imm<1> h, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD Shift by immediate + virtual bool SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SIMD vector x indexed element + virtual bool SMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMLAL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool SMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMLSL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool MUL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool SDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLA_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLA_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLS_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMLS_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMUL_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMUL_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLAL_elt_1(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLAL_elt_2(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLSL_elt_1(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FMLSL_elt_2(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool MLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool UMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool MLS_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool UMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool UMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQRDMLAH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool UDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool SQRDMLSH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, + Vec Vn, Vec Vd) { + return false; + } + virtual bool FMULX_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMULX_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, + Vec Vd) { + return false; + } + virtual bool FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<2> rot, + Imm<1> H, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Cryptographic three register + virtual bool SM3TT1A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3TT1B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3TT2A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3TT2B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SHA512 three register + virtual bool SHA512H(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA512H2(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SHA512SU1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool RAX1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool XAR(Vec Vm, Imm<6> imm6, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3PARTW1(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3PARTW2(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM4EKEY(Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Cryptographic four register + virtual bool EOR3(Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + virtual bool BCAX(Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + virtual bool SM3SS1(Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - SHA512 two register + virtual bool SHA512SU0(Vec Vn, Vec Vd) { + return false; + } + virtual bool SM4E(Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Conversion between floating point and fixed point + virtual bool SCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) { + return false; + } + virtual bool UCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) { + return false; + } + virtual bool FCVTZS_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTZU_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) { + return false; + } + + // Data Processing - FP and SIMD - Conversion between floating point and integer + virtual bool FCVTNS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTNU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool SCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) { + return false; + } + virtual bool UCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) { + return false; + } + virtual bool FCVTAS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTAU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FMOV_float_gen(bool sf, Imm<2> type, Imm<1> rmode_0, Imm<1> opc_0, size_t n, + size_t d) { + return false; + } + virtual bool FCVTPS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTPU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTMS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTMU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTZS_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FCVTZU_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + return false; + } + virtual bool FJCVTZS(Vec Vn, Reg Rd) { + return false; + } + + // Data Processing - FP and SIMD - Floating point data processing + virtual bool FMOV_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FABS_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNEG_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSQRT_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTN_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTP_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTM_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTZ_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTA_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTX_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINTI_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT32X_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT64X_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT32Z_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + virtual bool FRINT64Z_float(Imm<2> type, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Floating point compare + virtual bool FCMP_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) { + return false; + } + virtual bool FCMPE_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) { + return false; + } + + // Data Processing - FP and SIMD - Floating point immediate + virtual bool FMOV_float_imm(Imm<2> type, Imm<8> imm8, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Floating point conditional compare + virtual bool FCCMP_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) { + return false; + } + virtual bool FCCMPE_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) { + return false; + } + + // Data Processing - FP and SIMD - Floating point data processing two register + virtual bool FMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FDIV_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FADD_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FSUB_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAX_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMIN_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMAXNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMINNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Floating point conditional select + virtual bool FCSEL_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Vec Vd) { + return false; + } + + // Data Processing - FP and SIMD - Floating point data processing three register + virtual bool FMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + virtual bool FMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } + virtual bool FNMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) { + return false; + } +}; + +} // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index 046ee53346..06543b0833 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "game_settings.h" @@ -249,30 +248,12 @@ struct System::Impl { } } - void NotifyNVDECChannelOpen(u64 process_id) { - std::scoped_lock lock{nvdec_active_mutex}; - ++nvdec_active_channels[process_id]; - } - - void NotifyNVDECChannelClose(u64 process_id) { - std::scoped_lock lock{nvdec_active_mutex}; - const auto it = nvdec_active_channels.find(process_id); - if (it == nvdec_active_channels.end()) { - return; - } - if (--it->second == 0) { - nvdec_active_channels.erase(it); - } + void SetNVDECActive(bool is_nvdec_active) { + nvdec_active = is_nvdec_active; } bool GetNVDECActive() { - std::scoped_lock lock{nvdec_active_mutex}; - return !nvdec_active_channels.empty(); - } - - bool IsNVDECActiveForProcess(u64 process_id) { - std::scoped_lock lock{nvdec_active_mutex}; - return nvdec_active_channels.contains(process_id); + return nvdec_active; } void InitializeDebugger(System& system, u16 port) { @@ -528,8 +509,6 @@ struct System::Impl { mutable std::mutex suspend_guard; std::mutex general_channel_mutex; - std::mutex nvdec_active_mutex; - std::unordered_map nvdec_active_channels; std::atomic_bool is_paused{}; std::atomic_bool is_shutting_down{}; std::atomic_bool is_powered_on{}; @@ -537,6 +516,7 @@ struct System::Impl { bool extended_memory_layout : 1 = false; bool exit_locked : 1 = false; bool exit_requested : 1 = false; + bool nvdec_active : 1 = false; void EnsureGeneralChannelInitialized(System& system) { if (!general_channel_event) { @@ -600,22 +580,14 @@ void System::UnstallApplication() { impl->UnstallApplication(); } -void System::NotifyNVDECChannelOpen(u64 process_id) { - impl->NotifyNVDECChannelOpen(process_id); -} - -void System::NotifyNVDECChannelClose(u64 process_id) { - impl->NotifyNVDECChannelClose(process_id); +void System::SetNVDECActive(bool is_nvdec_active) { + impl->SetNVDECActive(is_nvdec_active); } bool System::GetNVDECActive() { return impl->GetNVDECActive(); } -bool System::IsNVDECActiveForProcess(u64 process_id) { - return impl->IsNVDECActiveForProcess(process_id); -} - void System::InitializeDebugger() { impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); } diff --git a/src/core/core.h b/src/core/core.h index e26093f260..d88ed64151 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -191,10 +191,8 @@ public: std::unique_lock StallApplication(); void UnstallApplication(); - void NotifyNVDECChannelOpen(u64 process_id); - void NotifyNVDECChannelClose(u64 process_id); + void SetNVDECActive(bool is_nvdec_active); [[nodiscard]] bool GetNVDECActive(); - [[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id); /** * Initialize the debugger. diff --git a/src/core/crypto/ncz_decompressor.cpp b/src/core/crypto/ncz_decompressor.cpp new file mode 100644 index 0000000000..5f56e55375 --- /dev/null +++ b/src/core/crypto/ncz_decompressor.cpp @@ -0,0 +1,430 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/common_types.h" +#include "common/logging.h" +#include "common/zstd_compression.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/key_manager.h" +#include "core/crypto/ncz_decompressor.h" +#include "core/file_sys/vfs/vfs.h" + +namespace Core::Crypto { + +namespace { + +constexpr u64 HeaderSize = 0x4000; +constexpr std::size_t SectionWire = 0x40; +constexpr std::size_t BlockFixed = 0x18; +constexpr std::size_t Chunk = 0x100000; +// Upper bound on NCZ section entries (64 bytes each, so the table is at most 4 MiB). A base NCA +// has a handful of sections, but nsz stores an update's patch (BKTR) data one entry per +// subsection, which is thousands of them. +constexpr u64 MaxSectionCount = 0x10000; +constexpr std::array SectionsMagic{'N', 'C', 'Z', 'S', 'E', 'C', 'T', 'N'}; +constexpr std::array BlockMagic{'N', 'C', 'Z', 'B', 'L', 'O', 'C', 'K'}; + +struct Section { + s64 offset, size; + u64 crypto_type; + Key128 key, counter; + bool Ctr() const { return crypto_type == 3 || crypto_type == 4; } +}; + +struct BlockHeader { + u32 exponent; + u64 decompressed_size; + std::vector sizes; +}; + +u64 Le(const u8* p, std::size_t n) { + u64 v = 0; + for (std::size_t i = n; i-- > 0;) v = (v << 8) | p[i]; + return v; +} + +// Mirrors nsz AESCTR.seek(): upper 8 bytes = stored counter, lower 8 = absolute NCA offset / 16. +void SetIv(Key128& iv, const Key128& base, u64 off) { + iv = base; + u64 blk = off >> 4; + for (std::size_t i = 0; i < 8; ++i) { iv[15 - i] = static_cast(blk & 0xFF); blk >>= 8; } +} + +// Parse header + section table + optional block header. Returns payload_start. +std::optional, std::optional>> ParseHeader( + const FileSys::VirtualFile& f, u64& payload_start) { + const u64 fsize = f->GetSize(); + if (fsize < HeaderSize + 16) return std::nullopt; + + const auto head = f->ReadBytes(16, HeaderSize); + if (head.size() != 16 || !std::equal(head.begin(), head.begin() + 8, SectionsMagic.begin())) + return std::nullopt; + const u64 count = Le(head.data() + 8, 8); + if (count == 0 || count > MaxSectionCount || count * SectionWire > fsize - HeaderSize - 16) + return std::nullopt; + + const auto table = f->ReadBytes(count * SectionWire, HeaderSize + 16); + if (table.size() != count * SectionWire) return std::nullopt; + + std::vector
sections; + sections.reserve(count + 1); + for (u64 i = 0; i < count; ++i) { + const u8* e = table.data() + i * SectionWire; + Section s{}; + s.offset = static_cast(Le(e, 8)); + s.size = static_cast(Le(e + 8, 8)); + s.crypto_type = Le(e + 16, 8); + std::memcpy(s.key.data(), e + 32, 16); + std::memcpy(s.counter.data(), e + 48, 16); + sections.push_back(s); + } + + const u64 block_start = HeaderSize + 16 + count * SectionWire; + const auto fixed = f->ReadBytes(BlockFixed, block_start); + std::optional bh; + if (fixed.size() == BlockFixed && + std::equal(fixed.begin(), fixed.begin() + 8, BlockMagic.begin())) { + BlockHeader b{}; + b.exponent = fixed[11]; + const s32 n = static_cast(Le(fixed.data() + 12, 4)); + if (n <= 0) return std::nullopt; + b.decompressed_size = Le(fixed.data() + 16, 8); + const auto sizes = f->ReadBytes(static_cast(n) * 4, block_start + BlockFixed); + if (sizes.size() != static_cast(n) * 4) return std::nullopt; + b.sizes.resize(n); + for (s32 i = 0; i < n; ++i) b.sizes[i] = static_cast(Le(sizes.data() + i * 4, 4)); + payload_start = block_start + BlockFixed + static_cast(n) * 4; + bh = std::move(b); + } else { + payload_start = block_start; + } + + if (!sections.empty() && sections[0].offset > static_cast(HeaderSize)) { + Section fake{}; + fake.offset = static_cast(HeaderSize); + fake.size = sections[0].offset - static_cast(HeaderSize); + sections.insert(sections.begin(), fake); + } + return std::make_pair(std::move(sections), std::move(bh)); +} + +// Sequential reader over independently-compressed blocks, one block cached (like nsz_rs). +class BlockReader { +public: + BlockReader(FileSys::VirtualFile f, const BlockHeader& bh, u64 payload_start) + : source{std::move(f)}, block_size{1ULL << bh.exponent}, sizes{bh.sizes}, + decompressed_size{bh.decompressed_size} { + if (bh.exponent < 14 || bh.exponent > 32 || sizes.empty()) + throw std::runtime_error("bad NCZBLOCK header"); + u64 off = payload_start; + offsets.reserve(sizes.size()); + for (u32 s : sizes) { offsets.push_back(off); off += s; } + } + + // Fills `out` from `position`; returns bytes written. + std::size_t ReadAt(u64 position, std::span out) { + std::size_t done = 0; + while (done < out.size() && position < decompressed_size) { + const u64 id = position / block_size; + if (id >= sizes.size()) break; + if (cache_id != id && !Load(id)) break; + const u64 in = position - id * block_size; + const std::size_t n = static_cast( + std::min(out.size() - done, cache.size() - in)); + std::memcpy(out.data() + done, cache.data() + in, n); + done += n; + position += n; + } + return done; + } + +private: + bool Load(u64 id) { + u64 want = block_size; + if (id + 1 == sizes.size()) { + const u64 rem = decompressed_size % block_size; + if (rem > 0) want = rem; // avoid the 0-size bug from the original nsz + } + try { cache.resize(static_cast(want)); } + catch (const std::bad_alloc&) { return false; } + const u32 csize = sizes[id]; + if (csize < want) { + const auto comp = source->ReadBytes(csize, offsets[id]); + if (comp.size() != csize || + Common::Compression::DecompressDataZSTD(comp, cache) != want) return false; + } else if (source->Read(cache.data(), cache.size(), offsets[id]) != cache.size()) { + return false; + } + cache_id = id; + return true; + } + + FileSys::VirtualFile source; + u64 block_size; + std::vector offsets; + std::vector sizes; + u64 decompressed_size; + u64 cache_id = ~0ULL; + std::vector cache; +}; + +class BlockNczFile final : public FileSys::VfsFile { +public: + BlockNczFile(std::string name_, std::vector hdr_, std::vector
sections_, + std::unique_ptr body_) + : name{std::move(name_)}, hdr{std::move(hdr_)}, sections{std::move(sections_)}, + body{std::move(body_)} { + nca_size = HeaderSize; + for (const auto& s : sections) + nca_size = std::max(nca_size, static_cast(s.offset + s.size)); + ciphers.reserve(sections.size()); + for (const auto& s : sections) + ciphers.push_back(s.Ctr() ? std::make_unique>(s.key, Mode::CTR) + : nullptr); + } + + std::string GetName() const override { return name; } + std::size_t GetSize() const override { return static_cast(nca_size); } + bool Resize(std::size_t) override { return false; } + FileSys::VirtualDir GetContainingDirectory() const override { return nullptr; } + bool IsWritable() const override { return false; } + bool IsReadable() const override { return true; } + + std::size_t Read(u8* data, std::size_t len, std::size_t off) const override { + if (off >= nca_size) return 0; + len = static_cast(std::min(len, nca_size - off)); + const u64 end = off + len; + u64 pos = off; + if (pos < HeaderSize) { + const u64 n = std::min(end, HeaderSize) - pos; + std::memcpy(data, hdr.data() + pos, static_cast(n)); + pos += n; + } + std::scoped_lock lock{mutex}; + for (std::size_t i = 0; i < sections.size() && pos < end; ++i) { + const auto& s = sections[i]; + const u64 begin = std::max(static_cast(s.offset), HeaderSize); + const u64 stop = std::max(static_cast(s.offset + s.size), begin); + if (stop <= pos) continue; + if (pos < begin) { + const u64 g = std::min(end, begin); + std::memset(data + (pos - off), 0, static_cast(g - pos)); + pos = g; + if (pos >= end) break; + } + const u64 pe = std::min(end, stop); + const std::span piece(data + (pos - off), static_cast(pe - pos)); + if (body->ReadAt(pos - HeaderSize, piece) != piece.size()) + return static_cast(pos - off); + if (ciphers[i]) { + Key128 iv{}; + SetIv(iv, s.counter, pos); + ciphers[i]->SetIV(iv); + ciphers[i]->Transcode(piece.data(), piece.size(), piece.data(), Op::Encrypt); + } + pos = pe; + } + return len; + } + + std::size_t Write(const u8*, std::size_t, std::size_t) override { return 0; } + bool Rename(std::string_view) override { return false; } + +private: + const std::string name; + const std::vector hdr; + const std::vector
sections; + const std::unique_ptr body; + std::vector>> ciphers; + mutable std::mutex mutex; + u64 nca_size{}; +}; + +// Solid NCZ: one zstd stream over the whole body. Decoded incrementally into memory, up to the +// requested offset, so sequential install reads are O(n) instead of O(n^2). +class SolidNczFile final : public FileSys::VfsFile { +public: + SolidNczFile(std::string name_, FileSys::VirtualFile source_, + std::vector
sections_, u64 payload_start_) + : name{std::move(name_)}, source{std::move(source_)}, sections{std::move(sections_)}, + payload_start{payload_start_} { + const auto raw_header = source->ReadBytes(HeaderSize, 0); + data.assign(raw_header.begin(), raw_header.end()); + frontier = HeaderSize; + } + + ~SolidNczFile() override { + if (stream != nullptr) ZSTD_freeDStream(stream); + } + + std::string GetName() const override { return name; } + std::size_t GetSize() const override { + u64 size = HeaderSize; + for (const auto& s : sections) + size = std::max(size, static_cast(s.offset + s.size)); + return static_cast(size); + } + bool Resize(std::size_t) override { return false; } + FileSys::VirtualDir GetContainingDirectory() const override { return nullptr; } + bool IsWritable() const override { return false; } + bool IsReadable() const override { return true; } + + std::size_t Read(u8* out, std::size_t len, std::size_t off) const override { + std::scoped_lock lock{mutex}; + const u64 nca_size = GetSize(); + if (off >= nca_size) return 0; + len = static_cast(std::min(len, nca_size - off)); + const u64 needed = off + len; + if (needed > frontier && !DecodeTo(needed)) return 0; + std::memcpy(out, data.data() + off, len); + return len; + } + + std::size_t Write(const u8*, std::size_t, std::size_t) override { return 0; } + bool Rename(std::string_view) override { return false; } + +private: + bool DecodeTo(u64 needed) const { + if (stream == nullptr) { + stream = ZSTD_createDStream(); + if (stream == nullptr) return false; + ZSTD_DCtx_setParameter(stream, ZSTD_d_windowLogMax, 31); + read_pos = payload_start; + } + + const u64 file_size = source->GetSize(); + std::vector out_buf(Chunk); + + std::size_t index = 0; + for (; index < sections.size(); ++index) { + const auto& s = sections[index]; + const u64 begin = std::max(static_cast(s.offset), HeaderSize); + const u64 end = std::max(static_cast(s.offset) + static_cast(s.size), + begin); + if (end > frontier) break; + } + + for (; index < sections.size() && frontier < needed; ++index) { + const auto& s = sections[index]; + const u64 begin = std::max(static_cast(s.offset), HeaderSize); + const u64 end = std::max(static_cast(s.offset) + static_cast(s.size), + begin); + if (end <= frontier) continue; + u64 pos = std::max(begin, frontier); + + std::optional> cipher; + if (s.Ctr()) cipher.emplace(s.key, Mode::CTR); + + while (pos < end && frontier < needed) { + const u64 want = std::min(end - pos, out_buf.size()); + std::size_t produced = 0; + while (produced < want) { + if (in.pos >= in.size) { + const u64 remaining = file_size - read_pos; + const std::size_t chunk = static_cast( + std::min(Chunk, remaining)); + if (chunk == 0) { LOG_ERROR(Crypto, "Solid NCZ: truncated"); return false; } + const auto bytes = source->ReadBytes(chunk, read_pos); + if (bytes.empty()) { LOG_ERROR(Crypto, "Solid NCZ: truncated"); return false; } + read_pos += bytes.size(); + in_buf.assign(bytes.begin(), bytes.end()); + in.src = in_buf.data(); + in.size = in_buf.size(); + in.pos = 0; + } + ZSTD_outBuffer outb{out_buf.data() + produced, want - produced, 0}; + const std::size_t res = ZSTD_decompressStream(stream, &outb, &in); + if (ZSTD_isError(res)) { + LOG_ERROR(Crypto, "Solid NCZ: zstd error"); + return false; + } + produced += outb.pos; + } + if (cipher) { + Key128 iv{}; + SetIv(iv, s.counter, pos); + cipher->SetIV(iv); + cipher->Transcode(out_buf.data(), produced, out_buf.data(), Op::Encrypt); + } + data.insert(data.end(), out_buf.begin(), out_buf.begin() + produced); + pos += produced; + frontier = pos; + } + } + return frontier >= needed; + } + + const std::string name; + const FileSys::VirtualFile source; + const std::vector
sections; + const u64 payload_start; + mutable std::mutex mutex; + mutable std::vector data; + mutable ZSTD_DStream* stream = nullptr; + mutable u64 read_pos = 0; + mutable u64 frontier = 0; + mutable std::vector in_buf; + mutable ZSTD_inBuffer in{nullptr, 0, 0}; +}; + +// Shared opener: block path always; solid only when allow_solid is true. +FileSys::VirtualFile OpenNcz(const FileSys::VirtualFile& f, std::string_view name, + bool allow_solid) { + if (!f) return nullptr; + u64 ps = 0; + auto parsed = ParseHeader(f, ps); + if (!parsed) return nullptr; + auto& [sections, block] = *parsed; + + if (block) { + auto hdr = f->ReadBytes(HeaderSize, 0); + if (hdr.size() != HeaderSize) return nullptr; + std::unique_ptr body; + try { body = std::make_unique(f, *block, ps); } + catch (const std::exception& e) { + LOG_ERROR(Crypto, "Failed to open NCZBLOCK: {}", e.what()); + return nullptr; + } + return std::make_shared(std::string{name}, std::move(hdr), + std::move(sections), std::move(body)); + } + + if (allow_solid) { + return std::make_shared(std::string{name}, f, std::move(sections), ps); + } + + LOG_DEBUG(Crypto, "Solid NCZ {} skipped (only NCZBLOCK is supported for play)", name); + return nullptr; +} + +} // Anonymous namespace + +bool IsBlockCompressedNCZ(const FileSys::VirtualFile& f) { + if (!f) return false; + u64 ps = 0; + const auto p = ParseHeader(f, ps); + return p && p->second.has_value(); +} + +FileSys::VirtualFile DecompressNCZ(const FileSys::VirtualFile& f, std::string_view name) { + return OpenNcz(f, name, /*allow_solid=*/false); +} + +FileSys::VirtualFile DecompressNCZForInstall(const FileSys::VirtualFile& f, + std::string_view name) { + return OpenNcz(f, name, /*allow_solid=*/true); +} + +} // namespace Core::Crypto \ No newline at end of file diff --git a/src/core/crypto/ncz_decompressor.h b/src/core/crypto/ncz_decompressor.h new file mode 100644 index 0000000000..9557f8c517 --- /dev/null +++ b/src/core/crypto/ncz_decompressor.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "core/file_sys/vfs/vfs_types.h" + +namespace Core::Crypto { + +/** + * Opens an NCZBLOCK-compressed NCA (nsz -B / .xcz block format) as a VirtualFile backed by + * on-demand block decoding. Nothing is written to disk; each read decodes only the blocks it + * covers and re-encrypts CTR sections. Solid NCZ is not supported. + * + * @param ncz_file The raw NCZ payload (e.g. ".ncz" entry from an NSZ/XCZ PFS0). + * @param output_name Name to give the returned VirtualFile (e.g. ".nca"). + * + * @return a VirtualFile for the NCA, or nullptr if the NCZ is malformed or not NCZBLOCK. + */ +[[nodiscard]] FileSys::VirtualFile DecompressNCZ(const FileSys::VirtualFile& ncz_file, + std::string_view output_name); + +/** + * Returns true if the given file is an NCZ payload in block-compressed (NCZBLOCK) form, + * i.e. one that DecompressNCZ() can open. Solid NCZ returns false. + */ +[[nodiscard]] bool IsBlockCompressedNCZ(const FileSys::VirtualFile& ncz_file); + +// Install to NAND: same as DecompressNCZ, but solid NCZ is decoded on demand too. +[[nodiscard]] FileSys::VirtualFile DecompressNCZForInstall( + const FileSys::VirtualFile& ncz_file, std::string_view output_name); + +} // namespace Core::Crypto \ No newline at end of file diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 7603e94698..b5103e23bc 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -29,8 +29,11 @@ public: template Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const { - return (reinterpret_cast(ptr) - - reinterpret_cast(buffer.BackingBasePointer())) + + return GetPhysicalAddr(reinterpret_cast(ptr)); + } + + Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const { + return (ptr - reinterpret_cast(buffer.BackingBasePointer())) + DramMemoryMap::Base; } diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 72c8b7787f..b4b3b46088 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -18,11 +18,7 @@ #include "common/common_types.h" #include "common/range_mutex.h" #include "common/scratch_buffer.h" -#include "common/virtual_buffer.h" - -namespace Common { -class HostMemory; -} +#include "common/sparse_large_vector.h" namespace Core { @@ -130,10 +126,6 @@ public: // New batch API to update multiple ranges with a single lock acquisition. void UpdatePagesCachedBatch(std::span> ranges, s32 delta); - const Common::HostMemory& GetHostMemory() const noexcept { - return host_memory; - } - private: struct TranslationEntry { DAddr guest_page{}; @@ -179,7 +171,6 @@ private: std::unique_ptr> impl; const uintptr_t physical_base; - const Common::HostMemory& host_memory; DeviceInterface* device_inter; struct TrackedEntry { @@ -187,8 +178,8 @@ private: u32 continuity_tracker; u32 compressed_physical_ptr; }; - Common::VirtualBuffer compressed_device_addr; - Common::VirtualBuffer tracked_entries; + Common::SparseLargeVector compressed_device_addr; + Common::SparseLargeVector tracked_entries; // Process memory interfaces @@ -209,8 +200,8 @@ private: return std::make_pair(asid, address); } - void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { - tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit); + constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { + tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit); } std::array t_slot{}; diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 8f3ed53285..97c2706abb 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -171,24 +171,12 @@ struct DeviceMemoryManagerAllocator { template DeviceMemoryManager::DeviceMemoryManager(const DeviceMemory& device_memory_) : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} - , host_memory{device_memory_.buffer} , device_inter{nullptr} , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) { impl = std::make_unique>(); cached_pages = std::make_unique(); - - const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; - for (size_t i = 0; i < total_virtual; i++) { - tracked_entries[i].compressed_physical_ptr = 0; - tracked_entries[i].continuity_tracker = 1; - tracked_entries[i].cpu_backing_address = 0; - } - const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS); - for (size_t i = 0; i < total_phys; i++) { - compressed_device_addr[i] = 0; - } } template @@ -221,26 +209,28 @@ void DeviceMemoryManager::Map(DAddr address, VAddr virtual_address, size size_t start_page_d = address >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; std::scoped_lock lk(mapping_guard); + + tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); for (size_t i = 0; i < num_pages; i++) { const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); if (ptr == nullptr) [[unlikely]] { - tracked_entries[start_page_d + i].compressed_physical_ptr = 0; + tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0; continue; } auto phys_addr = static_cast(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; - tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr; + tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr; InsertCPUBacking(start_page_d + i, new_vaddress, asid); const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 new_dev = static_cast(start_page_d + i); if (base_dev == 0) [[likely]] { - compressed_device_addr[phys_addr - 1U] = new_dev; + compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev; continue; } u32 start_id = base_dev & MULTI_MASK; if ((base_dev >> MULTI_FLAG_BITS) == 0) { start_id = impl->multi_dev_address.Register(base_dev); - compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id; + compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id; } impl->multi_dev_address.Register(new_dev, start_id); } @@ -256,24 +246,26 @@ void DeviceMemoryManager::Unmap(DAddr address, size_t size) { size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; device_inter->InvalidateRegion(address, size); std::scoped_lock lk(mapping_guard); + + tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case for (size_t i = 0; i < num_pages; i++) { - auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr; - tracked_entries[start_page_d + i].compressed_physical_ptr = 0; - tracked_entries[start_page_d + i].cpu_backing_address = 0; + auto& entry = tracked_entries.GetUnchecked(start_page_d + i); + auto phys_addr = entry.compressed_physical_ptr; + entry.compressed_physical_ptr = 0; + entry.cpu_backing_address = 0; if (phys_addr != 0) [[likely]] { - const u32 base_dev = compressed_device_addr[phys_addr - 1U]; + u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U); if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { - compressed_device_addr[phys_addr - 1] = 0; + base_dev = 0; continue; } const auto [more_entries, new_start] = impl->multi_dev_address.Unregister( static_cast(start_page_d + i), base_dev & MULTI_MASK); if (!more_entries) { - compressed_device_addr[phys_addr - 1] = - impl->multi_dev_address.ReleaseEntry(new_start); + base_dev = impl->multi_dev_address.ReleaseEntry(new_start); continue; } - compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG; + base_dev = new_start | MULTI_FLAG; } } t_slot = {}; @@ -286,6 +278,8 @@ void DeviceMemoryManager::TrackContinuityImpl(DAddr address, VAddr virtu size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; uintptr_t last_ptr = 0; size_t page_count = 1; + + tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); for (size_t i = num_pages; i > 0; i--) { size_t index = i - 1; const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE; @@ -297,14 +291,14 @@ void DeviceMemoryManager::TrackContinuityImpl(DAddr address, VAddr virtu page_count = 1; } last_ptr = new_ptr; - tracked_entries[start_page_d + index].continuity_tracker = static_cast(page_count); + tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast(page_count) - 1; } } template u8* DeviceMemoryManager::GetSpan(const DAddr src_addr, const std::size_t size) { size_t page_index = src_addr >> page_bits; size_t subbits = src_addr & page_mask; - if ((static_cast(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { + if ((static_cast(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) { return GetPointer(src_addr); } return nullptr; @@ -314,7 +308,7 @@ template const u8* DeviceMemoryManager::GetSpan(const DAddr src_addr, const std::size_t size) const { size_t page_index = src_addr >> page_bits; size_t subbits = src_addr & page_mask; - if ((static_cast(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { + if ((static_cast(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) { return GetPointer(src_addr); } return nullptr; @@ -384,7 +378,7 @@ void DeviceMemoryManager::WalkBlock(DAddr addr, std::size_t size, auto o std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; while (remaining_size) { - const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker); + const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1); const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset); SCOPE_EXIT{ diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index b47b9437d0..1ba7bd3de3 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -45,6 +45,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base) // Ensure we have the proper key area keys to continue. const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration()); if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) { + LOG_ERROR(Loader, "Cannot decrypt {}: keys for gen 0x{:02X}+ required.", file->GetName(), reader->GetKeyGeneration()); status = Loader::ResultStatus::ErrorMissingKeyAreaKey; return; } diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 9d46549bf0..32e9041017 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -35,19 +35,22 @@ static IPSFileType IdentifyMagic(std::span magic) { } static bool IsEOF(IPSFileType type, std::span magic) { - return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0) - || (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0); + return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0) + || (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0); } VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { if (in == nullptr || ips == nullptr) return nullptr; - auto in_data = in->ReadAllBytes(); - auto const type = IdentifyMagic(in_data); + const auto type = IdentifyMagic(ips->ReadBytes(0x5)); if (type == IPSFileType::Error) return nullptr; + auto in_data = in->ReadAllBytes(); + if (in_data.size() == 0) + return nullptr; + std::vector temp(type == IPSFileType::IPS ? 3 : 4); u64 offset = 5; // After header while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { @@ -100,8 +103,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { struct IPSwitchRecord { - std::array data; - size_t count; + std::vector data; }; struct IPSwitchCompiler::IPSwitchPatch { ::Common::unordered_map records; @@ -122,22 +124,23 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) { IPSwitchRecord r{}; for (auto it = sv.cbegin(); it < sv.cend(); ) { if (*it == '\\' && it + 1 < sv.cend()) { - switch (it[1]) { - case 'a': r.data[r.count] = '\a'; break; - case 'b': r.data[r.count] = '\b'; break; - case 'e': r.data[r.count] = '\e'; break; - case 'f': r.data[r.count] = '\f'; break; - case 'n': r.data[r.count] = '\n'; break; - case 'r': r.data[r.count] = '\r'; break; - case 't': r.data[r.count] = '\t'; break; - case 'v': r.data[r.count] = '\v'; break; - case '?': r.data[r.count] = '\?'; break; - default: r.data[r.count] = it[1]; break; - } - ++r.count; + r.data.push_back([it]() { + switch (it[1]) { + case 'a': return '\a'; + case 'b': return '\b'; + case 'e': return '\e'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '?': return '\?'; + default: return it[1]; + } + }()); it += 2; } else { - ++r.count; + r.data.push_back(*it); ++it; } } @@ -223,8 +226,8 @@ void IPSwitchCompiler::Parse(std::span bytes) { if (start <= line.cend() && end <= line.cend()) { // Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun! auto const hs = Common::HexStringToVector({start, end}, is_little_endian); + r.data.resize(hs.size()); std::memcpy(r.data.data(), hs.data(), hs.size()); - r.count = hs.size(); LOG_INFO(Loader, "[H] value @ {:#08X}", offset); patches.back().records.insert_or_assign(u32(offset), std::move(r)); } else { @@ -293,7 +296,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { if (patch.enabled) { for (const auto& record : patch.records) { if (record.first < in_data.size()) { - auto replace_size = record.second.count; + auto replace_size = record.second.data.size(); if (record.first + replace_size > in_data.size()) replace_size = in_data.size() - record.first; std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size); diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index ada8c44e78..4fd7dc6c71 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -9,6 +9,7 @@ #include #include +#include "common/assert.h" #include "common/hex_util.h" #include "common/logging.h" #include "common/settings.h" @@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64 NACP nacp{nacp_file}; return nacp.GetVersionString(); } +YUZU_NO_INLINE std::optional GetParentApplicationId(const ContentProvider* provider, u64 title_id) { + return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id); +} } // Anonymous namespace PatchManager::PatchManager(u64 title_id_, const Service::FileSystem::FileSystemController& fs_controller_, const ContentProvider& content_provider_) - : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {} + : title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {} PatchManager::~PatchManager() = default; @@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const { return title_id; } +VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const { + const auto get_root = [&](u64 id) { + return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id); + }; + auto root = get_root(title_id); + if (!parent_title_id) { + return root; + } + std::vector roots{std::move(root), get_root(*parent_title_id)}; + std::erase(roots, nullptr); + return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots)); +} + +std::vector PatchManager::GetDisabledAddons() const { + auto disabled = Settings::values.disabled_addons[title_id]; + if (parent_title_id) { + const auto& shared = Settings::values.disabled_addons[*parent_title_id]; + disabled.insert(disabled.end(), shared.begin(), shared.end()); + } + return disabled; +} + VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); if (exefs == nullptr) return exefs; - const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto disabled = GetDisabledAddons(); bool update_disabled = true; std::optional enabled_version; @@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { } // LayeredExeFS - const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); - const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); + const auto load_dir = GetModificationLoadRoot(); + const auto sdmc_load_dir = GetModificationLoadRoot(true); std::vector patch_dirs = {sdmc_load_dir}; if (load_dir != nullptr) { @@ -346,7 +372,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { } std::vector PatchManager::CollectPatches(const std::vector& patch_dirs, const std::string& build_id) const { - const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto disabled = GetDisabledAddons(); const auto nso_build_id = fmt::format("{:0<64}", build_id); std::vector out; @@ -405,7 +431,7 @@ std::vector PatchManager::PatchNSO(const std::vector& nso, const std::st LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); - const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto load_dir = GetModificationLoadRoot(); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return nso; @@ -448,7 +474,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name) LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name); - const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto load_dir = GetModificationLoadRoot(); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return false; @@ -462,13 +488,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name) } std::vector PatchManager::CreateCheatList(const BuildID& build_id_) const { - const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto load_dir = GetModificationLoadRoot(); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); return {}; } - const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto disabled = GetDisabledAddons(); auto patch_dirs = load_dir->GetSubdirectories(); std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); }); @@ -502,17 +528,16 @@ std::vector PatchManager::CreateCheatList(const BuildI return out; } -static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, - const Service::FileSystem::FileSystemController& fs_controller) { - const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); - const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); +void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const { + const auto load_dir = GetModificationLoadRoot(); + const auto sdmc_load_dir = GetModificationLoadRoot(true); if ((type != ContentRecordType::Program && type != ContentRecordType::Data && type != ContentRecordType::HtmlDocument) || (load_dir == nullptr && sdmc_load_dir == nullptr)) { return; } - const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto disabled = GetDisabledAddons(); std::vector patch_dirs = load_dir->GetSubdirectories(); if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) { patch_dirs.push_back(sdmc_load_dir); @@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto disabled = GetDisabledAddons(); bool update_disabled = true; std::optional enabled_version; @@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs // LayeredFS if (apply_layeredfs) { - ApplyLayeredFS(romfs, title_id, type, fs_controller); + ApplyLayeredFS(romfs, type); } return romfs; @@ -1072,15 +1097,22 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw) const { std::optional PatchManager::GetGameVersion() const { const auto update_tid = GetUpdateTitleID(title_id); if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { - return content_provider.GetEntryVersion(update_tid); + const auto version = content_provider.GetEntryVersion(update_tid); + return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id)); } - return content_provider.GetEntryVersion(title_id); + const auto version = content_provider.GetEntryVersion(title_id); + return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id); } PatchManager::Metadata PatchManager::GetControlMetadata() const { const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control); if (base_control_nca == nullptr) { + if (parent_title_id) { + const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id); + const PatchManager parent{control_id, fs_controller, content_provider}; + return parent.GetControlMetadata(); + } return {}; } diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 5d71dbcf20..6819a1a3e7 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -109,10 +109,14 @@ public: [[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept; private: + [[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const; + [[nodiscard]] std::vector GetDisabledAddons() const; + void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const; [[nodiscard]] std::vector CollectPatches(const std::vector& patch_dirs, const std::string& build_id) const; u64 title_id; + std::optional parent_title_id; const Service::FileSystem::FileSystemController& fs_controller; const ContentProvider& content_provider; }; diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index d84c24f286..63ca9d3fe3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -147,10 +147,6 @@ ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { return npdm_header.address_space_type; } -void ProgramMetadata::SetAddressSpaceType(ProgramAddressSpaceType address_space) { - npdm_header.address_space_type.Assign(address_space); -} - u8 ProgramMetadata::GetMainThreadPriority() const { return npdm_header.main_thread_priority; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 7f5853567e..115e6d6cd1 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -76,7 +73,6 @@ public: bool Is64BitProgram() const; ProgramAddressSpaceType GetAddressSpaceType() const; - void SetAddressSpaceType(ProgramAddressSpaceType address_space); u8 GetMainThreadPriority() const; u8 GetMainThreadCore() const; u32 GetMainThreadStackSize() const; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index ce851e4d38..26e73d260a 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -15,6 +16,7 @@ #include "common/random.h" #include "common/string_util.h" #include "core/crypto/key_manager.h" +#include "core/crypto/ncz_decompressor.h" #include "core/file_sys/card_image.h" #include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" @@ -157,6 +159,25 @@ static std::shared_ptr OpenContainerAsNsp(const VirtualFile& file, Loader:: return nullptr; } +// Checks that every .ncz entry in the container is NCZBLOCK. Solid NCZ has no random access, +// and the whole point of this build is to reject it at scan time so such files never appear +// in the game list. Containers without any .ncz (plain NSP/XCI) pass through. +static bool ContainerIsFullyBlockCompressed(const std::shared_ptr& nsp) { + if (!nsp) { + return false; + } + for (const auto& entry : nsp->GetFiles()) { + const auto name = entry->GetName(); + if (name.size() < 4 || name.substr(name.size() - 4) != ".ncz") { + continue; + } + if (!Core::Crypto::IsBlockCompressedNCZ(entry)) { + return false; // solid NCZ -> reject the whole container + } + } + return true; // no .ncz at all, or all block-compressed +} + template bool ForEachContainerEntry(const std::shared_ptr& nsp, bool only_content, std::optional base_program_id, Callback&& on_entry) { @@ -348,6 +369,22 @@ std::vector ContentProvider::ListEntries() const { return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); } +std::optional ContentProvider::GetParentApplicationId(u64 program_id) const { + const auto application_id = GetBaseTitleID(program_id); + const auto program_index = program_id - application_id; + if (program_index == 0 || program_index > std::numeric_limits::max()) { + return std::nullopt; + } + if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) { + return std::nullopt; + } + if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program)) + || (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) { + return application_id; + } + return std::nullopt; +} + PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} bool PlaceholderCache::Create(const NcaID& id, u64 size) const { @@ -794,7 +831,14 @@ std::vector RegisteredCache::ListEntriesFilter( } static std::shared_ptr GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) { - auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false))); + const auto id_string = Common::HexToString(id, false); + auto file = nsp.GetFile(fmt::format("{}.nca", id_string)); + if (file == nullptr) { + if (auto ncz_file = nsp.GetFile(fmt::format("{}.ncz", id_string))) { + file = Core::Crypto::DecompressNCZForInstall( + ncz_file, fmt::format("{}.nca", id_string)); + } + } if (file == nullptr) { return nullptr; } @@ -1256,6 +1300,12 @@ bool ManualContentProvider::AddEntriesFromContainer(VirtualFile file, bool only_ return false; } + // Reject solid NCZ (.nsz without -B): only block-compressed NCZ is supported. + if (!ContainerIsFullyBlockCompressed(nsp)) { + LOG_DEBUG(Service_FS, "Skipping solid-compressed container: {}", file->GetName()); + return false; + } + return ForEachContainerEntry( nsp, only_content, base_program_id, [this](TitleType title_type, ContentRecordType content_type, u64 title_id, @@ -1398,8 +1448,9 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) { } const auto extension = Common::ToLower(filename.substr(dot_pos + 1)); - - if (extension == "nsp") { + // .xcz is an XCI container, but its updates only work through the NSP/PFS0 path + // (DecompressNCZ), not ProcessXCI. So route .xcz here on purpose. + if (extension == "nsp" || extension == "nsz" || extension == "xcz") { ProcessNSP(file); } else if (extension == "xci") { ProcessXCI(file); @@ -1417,6 +1468,12 @@ void ExternalContentProvider::ProcessNSP(const VirtualFile& file) { return; } + // Reject solid NCZ (.nsz without -B): Eden only supports block-compressed NCZ. + if (!ContainerIsFullyBlockCompressed(nsp)) { + LOG_DEBUG(Service_FS, "Skipping solid-compressed container: {}", file->GetName()); + return; + } + LOG_DEBUG(Service_FS, "Processing NSP file: {}", file->GetName()); AddExternalEntriesFromContainer(nsp, entries, versions, multi_version_entries); } diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index a8e60ad470..89140a2e95 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -100,6 +100,8 @@ public: std::optional title_type = {}, std::optional record_type = {}, std::optional title_id = {}) const = 0; + [[nodiscard]] std::optional GetParentApplicationId(u64 program_id) const; + protected: // A single instance of KeyManager to be used by GetEntry() Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 15dd4ec7de..c7899f023d 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 0ea323492b..1a39d819f6 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -12,6 +12,7 @@ #include "common/hex_util.h" #include "common/logging.h" #include "core/crypto/key_manager.h" +#include "core/crypto/ncz_decompressor.h" #include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" @@ -246,6 +247,17 @@ void NSP::ReadNCAs(const std::vector& files) { const auto id_string = Common::HexToString(rec.nca_id, false); auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); + if (next_file == nullptr) { + auto ncz_file = pfs->GetFile(fmt::format("{}.ncz", id_string)); + if (ncz_file != nullptr) { + next_file = Core::Crypto::DecompressNCZ(ncz_file, fmt::format("{}.nca", id_string)); + if (next_file == nullptr) { + LOG_DEBUG(Service_FS, "Skipping solid or unsupported NCZ content {}.ncz", id_string); + continue; + } + } + } + if (next_file == nullptr) { if (rec.type != ContentRecordType::DeltaFragment) { LOG_WARNING(Service_FS, diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index b04f832cd3..14e0caad3c 100644 --- a/src/core/file_sys/vfs/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -109,8 +109,7 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op auto reference = std::make_unique(); this->InsertReferenceIntoListLocked(*reference); - auto file = std::shared_ptr( - new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path))); + auto file = std::make_shared(*this, std::move(reference), path, perms, size, std::move(parent_path)); cache[path] = file; return file; @@ -177,7 +176,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); + return std::make_shared(*this, path, perms); } VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { @@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p if (!FS::CreateDirs(path)) { return nullptr; } - return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); + return std::make_shared(*this, path, perms); } VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h index 672126d7ba..a97f7d32ea 100644 --- a/src/core/file_sys/vfs/vfs_real.h +++ b/src/core/file_sys/vfs/vfs_real.h @@ -82,6 +82,9 @@ class RealVfsFile : public VfsFile { friend class RealVfsFilesystem; public: + RealVfsFile(RealVfsFilesystem& base, std::unique_ptr reference, + const std::string& path, OpenMode perms = OpenMode::Read, + std::optional size = {}, std::optional parent_path = {}); ~RealVfsFile() override; std::string GetName() const override; @@ -95,9 +98,6 @@ public: bool Rename(std::string_view name) override; private: - RealVfsFile(RealVfsFilesystem& base, std::unique_ptr reference, - const std::string& path, OpenMode perms = OpenMode::Read, - std::optional size = {}, std::optional parent_path = {}); RealVfsFilesystem& base; std::unique_ptr reference; @@ -113,6 +113,8 @@ class RealVfsDirectory : public VfsDirectory { friend class RealVfsFilesystem; public: + RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, + OpenMode perms = OpenMode::Read); ~RealVfsDirectory() override; VirtualFile GetFileRelative(std::string_view relative_path) const override; @@ -138,9 +140,6 @@ public: std::map> GetEntries() const override; private: - RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, - OpenMode perms = OpenMode::Read); - template std::vector> IterateEntries() const; diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e495e8978f..ee7a7693b2 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -74,7 +74,7 @@ public: }; /// Called from GPU thread when a frame is displayed. - virtual void OnFrameDisplayed([[maybe_unused]] u32 presented_frames) {} + virtual void OnFrameDisplayed() {} /** * Returns a GraphicsContext that the frontend provides to be used for rendering. diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index a727355a9f..2a0a7d49d5 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -635,6 +635,36 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st R_SUCCEED(); } +bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context, + Common::ProcessAddress address) const { + out_context->next_offset = GetInteger(address); + out_context->next_page = GetInteger(address) >> PageBits; + + return ContinueTraversal(impl, out_entry, out_context); +} + +bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, + TraversalContext *context) const { + // Setup invalid defaults. + out_entry->phys_addr = 0; + out_entry->block_size = PageSize; + // Validate that we can read the actual entry. + if (auto const page = context->next_page; page < impl.entries.size()) { + // Validate that the entry is mapped. + if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) { + // Populate the results and return true + out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset)); + context->next_page += 1; + context->next_offset += PageSize; + return true; + } + } + context->next_page += 1; + context->next_offset += PageSize; + // Otherwise return false + return false; +} + Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, @@ -940,7 +970,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a size_t tot_size = 0; next_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start); next_entry.block_size = (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1))); @@ -976,7 +1006,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a break; } - next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); } // Check the last entry. @@ -1754,7 +1784,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_ // Begin traversal. TraversalContext context; TraversalEntry next_entry; - R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr), + R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr), ResultInvalidCurrentMemory); // Prepare tracking variables. @@ -1764,7 +1794,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_ // Iterate, adding to group as we go. while (tot_size < size) { - R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)), + R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)), ResultInvalidCurrentMemory); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -1828,7 +1858,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr // Begin traversal. TraversalContext context; TraversalEntry next_entry; - if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) { + if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) { return false; } @@ -1839,7 +1869,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr // Iterate, comparing expected to actual. while (tot_size < size) { - if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) { + if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) { return false; } @@ -1896,7 +1926,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState( // Begin a traversal. TraversalContext context; TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; - R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address), + R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address), ResultInvalidCurrentMemory); // Traverse until we have enough size or we aren't contiguous any more. @@ -1905,7 +1935,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState( for (contig_size = cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); contig_size < size; contig_size += cur_entry.block_size) { - if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) { + if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) { break; } if (cur_entry.phys_addr != phys_address + contig_size) { @@ -2334,7 +2364,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr); + BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); // Set tracking variables. @@ -2345,7 +2375,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, while (true) { // Continue the traversal. traverse_valid = - m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context)); if (!traverse_valid) { break; } @@ -2567,7 +2597,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre TraversalContext context; TraversalEntry next_entry; ASSERT( - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address)); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address)); // Check that the physical region matches. R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion); @@ -2577,7 +2607,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); checked_size < size; checked_size += next_entry.block_size) { // Continue the traversal. - ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))); + ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))); // Check that the physical region matches. R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion); @@ -3029,7 +3059,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_ TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); // Prepare tracking variables. @@ -3041,7 +3071,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_ while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3129,7 +3159,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); // Prepare tracking variables. @@ -3167,7 +3197,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3225,7 +3255,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); // Prepare tracking variables. @@ -3267,7 +3297,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3728,7 +3758,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser( TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr); ASSERT(traverse_valid); // Prepare tracking variables. @@ -3768,7 +3798,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser( while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3822,7 +3852,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel( TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr); ASSERT(traverse_valid); // Prepare tracking variables. @@ -3845,7 +3875,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel( while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3902,7 +3932,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear( TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr); ASSERT(traverse_valid); // Prepare tracking variables. @@ -3941,7 +3971,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear( while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -3997,7 +4027,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si TraversalContext context; TraversalEntry next_entry; bool traverse_valid = - impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr); + BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr); ASSERT(traverse_valid); // Prepare tracking variables. @@ -4020,7 +4050,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si while (tot_size < size) { // Continue the traversal. traverse_valid = - impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); if (next_entry.phys_addr != (cur_addr + cur_size)) { @@ -4089,10 +4119,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap( bool traverse_valid; // Begin traversal. - traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry), + traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry), std::addressof(src_context), src_addr); ASSERT(traverse_valid); - traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry), + traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry), std::addressof(dst_context), dst_addr); ASSERT(traverse_valid); @@ -4127,7 +4157,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap( if (ofs + cur_copy_size != size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { // Continue the src traversal. - traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry), + traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry), std::addressof(src_context)); ASSERT(traverse_valid); @@ -4138,7 +4168,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap( if (cur_dst_addr + cur_min_size == dst_next_entry.phys_addr + dst_next_entry.block_size) { // Continue the dst traversal. - traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry), + traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry), std::addressof(dst_context)); ASSERT(traverse_valid); @@ -4223,10 +4253,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination( bool traverse_valid; // Begin traversal. - traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry), + traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry), std::addressof(src_context), src_addr); ASSERT(traverse_valid); - traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry), + traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry), std::addressof(dst_context), dst_addr); ASSERT(traverse_valid); @@ -4261,7 +4291,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination( if (ofs + cur_copy_size != size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { // Continue the src traversal. - traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry), + traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry), std::addressof(src_context)); ASSERT(traverse_valid); @@ -4272,7 +4302,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination( if (cur_dst_addr + cur_min_size == dst_next_entry.phys_addr + dst_next_entry.block_size) { // Continue the dst traversal. - traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry), + traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry), std::addressof(dst_context)); ASSERT(traverse_valid); @@ -4547,7 +4577,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // Begin traversal. TraversalContext context; TraversalEntry next_entry; - bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry), + bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry), std::addressof(context), aligned_src_start); ASSERT(traverse_valid); @@ -4597,7 +4627,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // If the block's size was one page, we may need to continue traversal. if (cur_block_size == 0 && aligned_src_size > PageSize) { traverse_valid = - src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); cur_block_addr = next_entry.phys_addr; @@ -4610,7 +4640,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, while (aligned_src_start + tot_block_size < mapping_src_end) { // Continue the traversal. traverse_valid = - src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); // Process the block. @@ -4653,7 +4683,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, if (mapped_block_end + cur_block_size < aligned_src_end && cur_block_size == last_block_size) { traverse_valid = - src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); ASSERT(traverse_valid); cur_block_addr = next_entry.phys_addr; @@ -5601,7 +5631,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size) : m_pt(pt), m_remaining_size(size) { // Begin a traversal. - ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry), + ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry), std::addressof(m_context), address)); // Setup tracking fields. @@ -5632,7 +5662,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si void DetermineContiguousBlockExtents() { // Continue traversing until we're not contiguous, or we have enough. while (m_cur_size < m_remaining_size) { - ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry), + ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry), std::addressof(m_context))); // If we're not contiguous, we're done. diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h index 1d4fa85f04..0c222910fc 100644 --- a/src/core/hle/kernel/k_page_table_base.h +++ b/src/core/hle/kernel/k_page_table_base.h @@ -370,6 +370,10 @@ private: size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const; + bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context, + Common::ProcessAddress address) const; + bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const; + Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, @@ -474,7 +478,14 @@ private: // Validate pre-conditions. ASSERT(this->IsLockedByCurrentThread()); - return this->GetImpl().GetPhysicalAddress(out, virt_addr); + if (virt_addr > (1ULL << m_address_space_width)) { + return false; + } + + *out = m_system.DeviceMemory().GetPhysicalAddr( + this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr)); + + return true; } public: diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 8e629f6456..3156a17064 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -465,6 +465,10 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) { // Check if we need scheduling. If we do, then we can't complete the switch and should // retry. if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { + // Some libc++ lazily init mutex + [[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock(); + DEBUG_ASSERT(!can_lock); + // Our switch failed. // We should unlock the thread context, and then retry. highest_priority_thread->m_context_guard.unlock(); @@ -496,6 +500,10 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) { // Check if the thread is terminated by checking the DPC flags. if ((thread->GetStackParameters().dpc_flags & static_cast(DpcFlag::Terminated)) == 0) { + // Some libc++ lazily init mutex + [[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock(); + DEBUG_ASSERT(!can_lock); + // The thread isn't terminated, so we want to unlock it. thread->m_context_guard.unlock(); } diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index c91e7969a2..a5dbab9571 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -12,7 +12,6 @@ #include "common/atomic_ops.h" #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/spin_lock.h" namespace Kernel { @@ -30,7 +29,7 @@ public: }; public: - constexpr KSlabHeapImpl() = default; + KSlabHeapImpl() = default; void Initialize() { ASSERT(m_head == nullptr); @@ -68,7 +67,7 @@ public: private: std::atomic m_head{}; - Common::SpinLock m_lock; + std::mutex m_lock; }; } // namespace impl diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 95ba1b4d76..0ee5f72c8e 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -19,7 +19,6 @@ #include "common/intrusive_red_black_tree.h" #include "common/scratch_buffer.h" -#include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_light_lock.h" @@ -920,7 +919,7 @@ private: bool m_resource_limit_release_hint{}; bool m_is_kernel_address_key{}; StackParameters m_stack_parameters{}; - Common::SpinLock m_context_guard{}; + std::mutex m_context_guard{}; // For emulation std::shared_ptr m_host_context{}; diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h index d7bffc055e..c50537d4ae 100644 --- a/src/core/hle/service/acc/async_context.h +++ b/src/core/hle/service/acc/async_context.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h index 433ebfe9d6..b7cf8b5f93 100644 --- a/src/core/hle/service/acc/errors.h +++ b/src/core/hle/service/acc/errors.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 1fd0cc700f..81688175c2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -23,6 +23,12 @@ constexpr std::size_t profile_username_size{32}; using ProfileUsername = std::array; using UserIDArray = std::array; +// This is nn::account::Uid +struct Uid { + std::array unk0; +}; +static_assert(sizeof(Uid) == 0x10); + /// Contains extra data related to a user. /// TODO: RE this structure struct UserData { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 2ef393439b..86d9d52e34 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 1afe253aed..0bf5880f30 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index 2b0714adf7..cd3035de31 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/frontend/applet_cabinet.h b/src/core/hle/service/am/frontend/applet_cabinet.h index 3a211ed372..a74f233569 100644 --- a/src/core/hle/service/am/frontend/applet_cabinet.h +++ b/src/core/hle/service/am/frontend/applet_cabinet.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_controller.h b/src/core/hle/service/am/frontend/applet_controller.h index 341ebed234..3877a7b02e 100644 --- a/src/core/hle/service/am/frontend/applet_controller.h +++ b/src/core/hle/service/am/frontend/applet_controller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project diff --git a/src/core/hle/service/am/frontend/applet_error.h b/src/core/hle/service/am/frontend/applet_error.h index 678bf33fac..af3e2134a5 100644 --- a/src/core/hle/service/am/frontend/applet_error.h +++ b/src/core/hle/service/am/frontend/applet_error.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_general.h b/src/core/hle/service/am/frontend/applet_general.h index eaa7ae25fe..ceb0537119 100644 --- a/src/core/hle/service/am/frontend/applet_general.h +++ b/src/core/hle/service/am/frontend/applet_general.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_mii_edit.h b/src/core/hle/service/am/frontend/applet_mii_edit.h index 5db792f7d2..8654426431 100644 --- a/src/core/hle/service/am/frontend/applet_mii_edit.h +++ b/src/core/hle/service/am/frontend/applet_mii_edit.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_mii_edit_types.h b/src/core/hle/service/am/frontend/applet_mii_edit_types.h index 23d9d7a69b..9c354bd947 100644 --- a/src/core/hle/service/am/frontend/applet_mii_edit_types.h +++ b/src/core/hle/service/am/frontend/applet_mii_edit_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_net_connect.h b/src/core/hle/service/am/frontend/applet_net_connect.h index d5d503ed41..0986b63c60 100644 --- a/src/core/hle/service/am/frontend/applet_net_connect.h +++ b/src/core/hle/service/am/frontend/applet_net_connect.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/am/frontend/applet_profile_select.cpp b/src/core/hle/service/am/frontend/applet_profile_select.cpp index 89b5a1eabe..0f46ee6232 100644 --- a/src/core/hle/service/am/frontend/applet_profile_select.cpp +++ b/src/core/hle/service/am/frontend/applet_profile_select.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_profile_select.h b/src/core/hle/service/am/frontend/applet_profile_select.h index 674e7afe17..4feba73a37 100644 --- a/src/core/hle/service/am/frontend/applet_profile_select.h +++ b/src/core/hle/service/am/frontend/applet_profile_select.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard_types.h b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h index a25ff2a6d2..6bb8f437d3 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard_types.h +++ b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/frontend/applet_web_browser.h b/src/core/hle/service/am/frontend/applet_web_browser.h index ae62389f13..41461a93d0 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.h +++ b/src/core/hle/service/am/frontend/applet_web_browser.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index 407ca10602..c59e4b9c6b 100644 --- a/src/core/hle/service/am/frontend/applets.cpp +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -29,6 +29,7 @@ #include "core/hle/service/am/frontend/applet_web_browser.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/sm/sm.h" namespace Service::AM::Frontend { @@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr storage) { void FrontendApplet::Exit() { auto applet_ = applet.lock(); - - std::scoped_lock lk{applet_->lock}; - applet_->is_completed = true; - applet_->state_changed_event.Signal(system.Kernel()); + { + std::scoped_lock lk{applet_->lock}; + applet_->is_completed = true; + applet_->state_changed_event.Signal(system.Kernel()); + } + if (auto caller_applet = applet_->caller_applet.lock()) { + std::scoped_lock lk{caller_applet->lock}; + std::erase(caller_applet->child_applets, applet_); + } + if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate(); } FrontendAppletSet::FrontendAppletSet() = default; @@ -243,9 +250,10 @@ std::shared_ptr FrontendAppletHolder::GetApplet(std::shared_ptr< return std::make_shared(system, applet, mode, *frontend.photo_viewer); case AppletId::NetConnect: return std::make_shared(system, applet, mode, *frontend.net_connect); + case AppletId::None: + return nullptr; default: - LOG_ERROR(Service_AM, "No backend implementation exists for applet_id={:02X} program_id={:016X}" - "Falling back to stub applet", static_cast(id), applet->program_id); + UNIMPLEMENTED_MSG("No frontend implementation exists for applet_id={:02X} program_id={:016X}! Using stub applet.", static_cast(id), applet->program_id); return std::make_shared(system, applet, id, mode); } } diff --git a/src/core/hle/service/am/frontend/applets.h b/src/core/hle/service/am/frontend/applets.h index 8eb08ea77b..6259a5668a 100644 --- a/src/core/hle/service/am/frontend/applets.h +++ b/src/core/hle/service/am/frontend/applets.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/am/lifecycle_manager.h b/src/core/hle/service/am/lifecycle_manager.h index ae041d3e29..15657a36d8 100644 --- a/src/core/hle/service/am/lifecycle_manager.h +++ b/src/core/hle/service/am/lifecycle_manager.h @@ -92,6 +92,13 @@ public: } } + void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) { + if (m_focus_state_changed_notification_enabled) { + m_has_focus_state_changed = true; + this->SignalSystemEventIfNeeded(kernel); + } + } + void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel); public: diff --git a/src/core/hle/service/am/process_holder.cpp b/src/core/hle/service/am/process_holder.cpp index 21ef5bf838..8ad6599fd6 100644 --- a/src/core/hle/service/am/process_holder.cpp +++ b/src/core/hle/service/am/process_holder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/process_holder.h b/src/core/hle/service/am/process_holder.h index 3a9b81dfb4..dbf04668dc 100644 --- a/src/core/hle/service/am/process_holder.h +++ b/src/core/hle/service/am/process_holder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/application_accessor.h b/src/core/hle/service/am/service/application_accessor.h index b9797bcc0a..7908099916 100644 --- a/src/core/hle/service/am/service/application_accessor.h +++ b/src/core/hle/service/am/service/application_accessor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/application_creator.h b/src/core/hle/service/am/service/application_creator.h index 75975b9596..59ae924131 100644 --- a/src/core/hle/service/am/service/application_creator.h +++ b/src/core/hle/service/am/service/application_creator.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index 215d204098..90d292c4a2 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out out_became_running) { Result IApplicationFunctions::GetPseudoDeviceId(Out out_pseudo_device_id) { LOG_WARNING(Service_AM, "(stubbed)"); - R_UNLESS(out_pseudo_device_id, ResultUnknown); + R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown); // This should be hashed with the device specific hash // for now this will do const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id); - u8 hash[EVP_MAX_MD_SIZE]; + R_UNLESS(res.first != nullptr, ResultUnknown); + std::array hash; unsigned int hash_len = 0; auto const seed = res.first->raw.seed_for_pseudo_device_id; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); auto const algorithm = EVP_sha1(); EVP_DigestInit_ex(ctx, algorithm, nullptr); EVP_DigestUpdate(ctx, &seed, sizeof(seed)); - EVP_DigestFinal_ex(ctx, hash, &hash_len); + EVP_DigestFinal_ex(ctx, hash.data(), &hash_len); EVP_MD_CTX_free(ctx); - *out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span{hash, std::size(hash)}); + *out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span{hash.begin(), hash.begin() + 16}); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp index 6e1328fee7..df438e1e17 100644 --- a/src/core/hle/service/am/service/application_proxy.cpp +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h index 8c62459c45..b56f597605 100644 --- a/src/core/hle/service/am/service/application_proxy.h +++ b/src/core/hle/service/am/service/application_proxy.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp index b7d7b3c2d9..7727951d8c 100644 --- a/src/core/hle/service/am/service/application_proxy_service.cpp +++ b/src/core/hle/service/am/service/application_proxy_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h index e5f4ea345b..fce860a7c1 100644 --- a/src/core/hle/service/am/service/application_proxy_service.h +++ b/src/core/hle/service/am/service/application_proxy_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/audio_controller.cpp b/src/core/hle/service/am/service/audio_controller.cpp index f627b1df0e..4e9af756f2 100644 --- a/src/core/hle/service/am/service/audio_controller.cpp +++ b/src/core/hle/service/am/service/audio_controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/service/audio_controller.h b/src/core/hle/service/am/service/audio_controller.h index 4b0f3f9aed..a13f3b199b 100644 --- a/src/core/hle/service/am/service/audio_controller.h +++ b/src/core/hle/service/am/service/audio_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.cpp b/src/core/hle/service/am/service/cradle_firmware_updater.cpp index 0a8af0858d..b9f19e59d2 100644 --- a/src/core/hle/service/am/service/cradle_firmware_updater.cpp +++ b/src/core/hle/service/am/service/cradle_firmware_updater.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.h b/src/core/hle/service/am/service/cradle_firmware_updater.h index 3e803f0ae9..c895a34c22 100644 --- a/src/core/hle/service/am/service/cradle_firmware_updater.h +++ b/src/core/hle/service/am/service/cradle_firmware_updater.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/debug_functions.cpp b/src/core/hle/service/am/service/debug_functions.cpp index fcac4776db..4128366270 100644 --- a/src/core/hle/service/am/service/debug_functions.cpp +++ b/src/core/hle/service/am/service/debug_functions.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/debug_functions.h b/src/core/hle/service/am/service/debug_functions.h index d559687439..381499e93d 100644 --- a/src/core/hle/service/am/service/debug_functions.h +++ b/src/core/hle/service/am/service/debug_functions.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/display_controller.h b/src/core/hle/service/am/service/display_controller.h index 171feace6e..35e98b9ad3 100644 --- a/src/core/hle/service/am/service/display_controller.h +++ b/src/core/hle/service/am/service/display_controller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/service/global_state_controller.cpp b/src/core/hle/service/am/service/global_state_controller.cpp index 3e3b61b0a0..e7cd60927c 100644 --- a/src/core/hle/service/am/service/global_state_controller.cpp +++ b/src/core/hle/service/am/service/global_state_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/global_state_controller.h b/src/core/hle/service/am/service/global_state_controller.h index 83efb57dfc..012692ecd8 100644 --- a/src/core/hle/service/am/service/global_state_controller.h +++ b/src/core/hle/service/am/service/global_state_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp index c49b1f3f05..849e5794cf 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer storage) { Result ILibraryAppletAccessor::PopOutData(Out> out_storage) { LOG_DEBUG(Service_AM, "called"); - if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) { - caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel()); - caller_applet->lifecycle_manager.RequestResumeNotification(); - caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel()); - caller_applet->lifecycle_manager.UpdateRequestedFocusState(); - } - R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get())); + if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) { + std::scoped_lock lk{caller_applet->lock}; + const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState(); + const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication(); + if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel()); + else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel()); + } if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) { auto impl = (*out_storage)->GetImpl(); diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index ce80ce946f..4ee92908b5 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -122,7 +122,10 @@ std::shared_ptr CreateGuestApplet(Core::System& system, auto broker = std::make_shared(system); applet->caller_applet = caller_applet; applet->caller_applet_broker = broker; - caller_applet->child_applets.push_back(applet); + { + std::scoped_lock lk{caller_applet->lock}; + caller_applet->child_applets.push_back(applet); + } window_system.TrackApplet(applet, false); return std::make_shared(system, broker, applet); } @@ -148,10 +151,10 @@ std::shared_ptr CreateFrontendApplet(Core::System& syste applet->caller_applet = caller_applet; applet->caller_applet_broker = storage; applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); - caller_applet->child_applets.push_back(applet); - - window_system.TrackApplet(applet, false); - + { + std::scoped_lock lk{caller_applet->lock}; + caller_applet->child_applets.push_back(applet); + } return std::make_shared(system, storage, applet); } diff --git a/src/core/hle/service/am/service/library_applet_creator.h b/src/core/hle/service/am/service/library_applet_creator.h index 34e7586e8f..1c7f930055 100644 --- a/src/core/hle/service/am/service/library_applet_creator.h +++ b/src/core/hle/service/am/service/library_applet_creator.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp index f9cfb82a90..567ff75797 100644 --- a/src/core/hle/service/am/service/library_applet_proxy.cpp +++ b/src/core/hle/service/am/service/library_applet_proxy.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/library_applet_proxy.h b/src/core/hle/service/am/service/library_applet_proxy.h index 792d585827..c659dbe187 100644 --- a/src/core/hle/service/am/service/library_applet_proxy.h +++ b/src/core/hle/service/am/service/library_applet_proxy.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/lock_accessor.h b/src/core/hle/service/am/service/lock_accessor.h index 9bfb5c0502..fe3f11dcf0 100644 --- a/src/core/hle/service/am/service/lock_accessor.h +++ b/src/core/hle/service/am/service/lock_accessor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/overlay_applet_proxy.cpp b/src/core/hle/service/am/service/overlay_applet_proxy.cpp index 5e140fe031..735ba9d307 100644 --- a/src/core/hle/service/am/service/overlay_applet_proxy.cpp +++ b/src/core/hle/service/am/service/overlay_applet_proxy.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/am/service/common_state_getter.h" diff --git a/src/core/hle/service/am/service/overlay_applet_proxy.h b/src/core/hle/service/am/service/overlay_applet_proxy.h index 2c7fa57dfd..6c02166a2d 100644 --- a/src/core/hle/service/am/service/overlay_applet_proxy.h +++ b/src/core/hle/service/am/service/overlay_applet_proxy.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/am/service/storage.h b/src/core/hle/service/am/service/storage.h index cde2ed0ead..462c823206 100644 --- a/src/core/hle/service/am/service/storage.h +++ b/src/core/hle/service/am/service/storage.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/storage_accessor.cpp b/src/core/hle/service/am/service/storage_accessor.cpp index 84577fee45..2fe379a74d 100644 --- a/src/core/hle/service/am/service/storage_accessor.cpp +++ b/src/core/hle/service/am/service/storage_accessor.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/storage_accessor.h b/src/core/hle/service/am/service/storage_accessor.h index 1a01730e00..76af3de363 100644 --- a/src/core/hle/service/am/service/storage_accessor.h +++ b/src/core/hle/service/am/service/storage_accessor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp index c435288a26..bb56750d09 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.cpp +++ b/src/core/hle/service/am/service/system_applet_proxy.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/system_applet_proxy.h b/src/core/hle/service/am/service/system_applet_proxy.h index ab9769b3d9..76cef507a5 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.h +++ b/src/core/hle/service/am/service/system_applet_proxy.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp index 54396affbe..f2b9dccb27 100644 --- a/src/core/hle/service/am/service/window_controller.cpp +++ b/src/core/hle/service/am/service/window_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/service/window_controller.h b/src/core/hle/service/am/service/window_controller.h index a784dd4a49..51a67f3039 100644 --- a/src/core/hle/service/am/service/window_controller.h +++ b/src/core/hle/service/am/service/window_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index 3858791430..fbb1a57efd 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b // Layer ordering. Composition sorts back-to-front. Now with enums for calrity. s32 z_index = Background; if (is_overlay) { - z_index = Overlay; + z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background; } else if (inherited_foreground) { z_index = is_obscured ? Foreground : ForegroundVisible; } diff --git a/src/core/hle/service/aoc/addon_content_manager.h b/src/core/hle/service/aoc/addon_content_manager.h index 91857df4c7..b505525590 100644 --- a/src/core/hle/service/aoc/addon_content_manager.h +++ b/src/core/hle/service/aoc/addon_content_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/aoc/purchase_event_manager.cpp b/src/core/hle/service/aoc/purchase_event_manager.cpp index 9e718510b4..64e1244967 100644 --- a/src/core/hle/service/aoc/purchase_event_manager.cpp +++ b/src/core/hle/service/aoc/purchase_event_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/aoc/purchase_event_manager.h b/src/core/hle/service/aoc/purchase_event_manager.h index ea3836bc93..c0bdfaf242 100644 --- a/src/core/hle/service/aoc/purchase_event_manager.h +++ b/src/core/hle/service/aoc/purchase_event_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index e188b4e446..265e581697 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h index 3357b7762a..c2f65a2f0e 100644 --- a/src/core/hle/service/apm/apm_controller.h +++ b/src/core/hle/service/apm/apm_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/apm/apm_interface.h b/src/core/hle/service/apm/apm_interface.h index 58718453b6..eeac3474b0 100644 --- a/src/core/hle/service/apm/apm_interface.h +++ b/src/core/hle/service/apm/apm_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 6caf794caa..fde766a275 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("audout:d", std::make_shared(system), 30); server_manager->RegisterNamedService("audin:d", std::make_shared(system), 30); server_manager->RegisterNamedService("audrec:d", std::make_shared(system), 30); - server_manager->RegisterNamedService("audren:d", std::make_shared(system), 30); + server_manager->RegisterNamedService("audren:d", std::make_shared(system), 30); server_manager->RegisterNamedService("audin:u", std::make_shared(system), 30); server_manager->RegisterNamedService("audin:a", std::make_shared(system), 30); diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index d70f022c73..eb8f482690 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index 611e48e8ba..9e2db4a84c 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -1,7 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h index ff51f4c244..a30b572ac7 100644 --- a/src/core/hle/service/audio/audio_controller.h +++ b/src/core/hle/service/audio/audio_controller.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/audio/audio_in.h b/src/core/hle/service/audio/audio_in.h index 3fe1e1e870..793d2b3ad4 100644 --- a/src/core/hle/service/audio/audio_in.h +++ b/src/core/hle/service/audio/audio_in.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_in_manager.cpp b/src/core/hle/service/audio/audio_in_manager.cpp index 2675a57735..fccfe38b3b 100644 --- a/src/core/hle/service/audio/audio_in_manager.cpp +++ b/src/core/hle/service/audio/audio_in_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_in_manager.h b/src/core/hle/service/audio/audio_in_manager.h index 2a983bc607..d8f63972c3 100644 --- a/src/core/hle/service/audio/audio_in_manager.h +++ b/src/core/hle/service/audio/audio_in_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_out.h b/src/core/hle/service/audio/audio_out.h index 779b213e78..178ee57df6 100644 --- a/src/core/hle/service/audio/audio_out.h +++ b/src/core/hle/service/audio/audio_out.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/audio_renderer.h b/src/core/hle/service/audio/audio_renderer.h index f25c50ce8d..7f4ea9a662 100644 --- a/src/core/hle/service/audio/audio_renderer.h +++ b/src/core/hle/service/audio/audio_renderer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/final_output_recorder_manager.cpp b/src/core/hle/service/audio/final_output_recorder_manager.cpp index f70a0e62d7..2bc9d29e3e 100644 --- a/src/core/hle/service/audio/final_output_recorder_manager.cpp +++ b/src/core/hle/service/audio/final_output_recorder_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/final_output_recorder_manager.h b/src/core/hle/service/audio/final_output_recorder_manager.h index 0663b894e7..833c621be7 100644 --- a/src/core/hle/service/audio/final_output_recorder_manager.h +++ b/src/core/hle/service/audio/final_output_recorder_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/final_output_recorder_manager_for_applet.cpp b/src/core/hle/service/audio/final_output_recorder_manager_for_applet.cpp index 7e2e42bbe7..dc2ea63aa0 100644 --- a/src/core/hle/service/audio/final_output_recorder_manager_for_applet.cpp +++ b/src/core/hle/service/audio/final_output_recorder_manager_for_applet.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/final_output_recorder_manager_for_applet.h b/src/core/hle/service/audio/final_output_recorder_manager_for_applet.h index 27940f7e0d..32efb75b5e 100644 --- a/src/core/hle/service/audio/final_output_recorder_manager_for_applet.h +++ b/src/core/hle/service/audio/final_output_recorder_manager_for_applet.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/hardware_opus_decoder.cpp b/src/core/hle/service/audio/hardware_opus_decoder.cpp index 03d3374c14..14888afa0d 100644 --- a/src/core/hle/service/audio/hardware_opus_decoder.cpp +++ b/src/core/hle/service/audio/hardware_opus_decoder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/hardware_opus_decoder.h b/src/core/hle/service/audio/hardware_opus_decoder.h index 511bf46bdb..5297572da8 100644 --- a/src/core/hle/service/audio/hardware_opus_decoder.h +++ b/src/core/hle/service/audio/hardware_opus_decoder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp b/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp index 9de72e30fe..b89e210012 100644 --- a/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp +++ b/src/core/hle/service/audio/hardware_opus_decoder_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/audio/hardware_opus_decoder_manager.h b/src/core/hle/service/audio/hardware_opus_decoder_manager.h index 4f869c5174..4dd211e232 100644 --- a/src/core/hle/service/audio/hardware_opus_decoder_manager.h +++ b/src/core/hle/service/audio/hardware_opus_decoder_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 8419399a62..645af9a386 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -6,12 +6,11 @@ #pragma once -#include #include #include +#include #include "common/common_types.h" -#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" #include "core/hle/service/bcat/bcat_types.h" #include "core/hle/service/kernel_helpers.h" diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index 2aaffc693b..65961baffe 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/bcat/bcat_result.h b/src/core/hle/service/bcat/bcat_result.h index edf8a65641..c6fa1b4e26 100644 --- a/src/core/hle/service/bcat/bcat_result.h +++ b/src/core/hle/service/bcat/bcat_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.h b/src/core/hle/service/bcat/delivery_cache_directory_service.h index b902c64959..cea67b0d87 100644 --- a/src/core/hle/service/bcat/delivery_cache_directory_service.h +++ b/src/core/hle/service/bcat/delivery_cache_directory_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.cpp b/src/core/hle/service/bcat/delivery_cache_file_service.cpp index b75fac4bfe..1d961e1655 100644 --- a/src/core/hle/service/bcat/delivery_cache_file_service.cpp +++ b/src/core/hle/service/bcat/delivery_cache_file_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.h b/src/core/hle/service/bcat/delivery_cache_file_service.h index e1012e6873..911bda55b3 100644 --- a/src/core/hle/service/bcat/delivery_cache_file_service.h +++ b/src/core/hle/service/bcat/delivery_cache_file_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.cpp b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp index 79e7e0d957..873977bf90 100644 --- a/src/core/hle/service/bcat/delivery_cache_progress_service.cpp +++ b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.h b/src/core/hle/service/bcat/delivery_cache_progress_service.h index f81a139806..83d4d35827 100644 --- a/src/core/hle/service/bcat/delivery_cache_progress_service.h +++ b/src/core/hle/service/bcat/delivery_cache_progress_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.cpp b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp index 46ed4dae67..a363bcb2d1 100644 --- a/src/core/hle/service/bcat/delivery_cache_storage_service.cpp +++ b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.h b/src/core/hle/service/bcat/delivery_cache_storage_service.h index 3b8dfb1a39..4849e58047 100644 --- a/src/core/hle/service/bcat/delivery_cache_storage_service.h +++ b/src/core/hle/service/bcat/delivery_cache_storage_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/builtin_news.h b/src/core/hle/service/bcat/news/builtin_news.h index cb06b2d4e3..e47cc89588 100644 --- a/src/core/hle/service/bcat/news/builtin_news.h +++ b/src/core/hle/service/bcat/news/builtin_news.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/bcat/news/msgpack.cpp b/src/core/hle/service/bcat/news/msgpack.cpp index b67b75ea9d..9cea2de522 100644 --- a/src/core/hle/service/bcat/news/msgpack.cpp +++ b/src/core/hle/service/bcat/news/msgpack.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/bcat/news/msgpack.h" diff --git a/src/core/hle/service/bcat/news/msgpack.h b/src/core/hle/service/bcat/news/msgpack.h index a7a2aa40ba..3163689782 100644 --- a/src/core/hle/service/bcat/news/msgpack.h +++ b/src/core/hle/service/bcat/news/msgpack.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp index ed393f7a26..b60de3b1f5 100644 --- a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.h b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h index 6cc9ae0997..5c2aeec182 100644 --- a/src/core/hle/service/bcat/news/newly_arrived_event_holder.h +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/news_data_service.h b/src/core/hle/service/bcat/news/news_data_service.h index 8f5e031701..45fe4bafcc 100644 --- a/src/core/hle/service/bcat/news/news_data_service.h +++ b/src/core/hle/service/bcat/news/news_data_service.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp index 3580ceb0f7..27ca1ad378 100644 --- a/src/core/hle/service/bcat/news/news_database_service.cpp +++ b/src/core/hle/service/bcat/news/news_database_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h index 143864e80e..7552da23de 100644 --- a/src/core/hle/service/bcat/news/news_database_service.h +++ b/src/core/hle/service/bcat/news/news_database_service.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp index aed1536bfe..4dcaa14881 100644 --- a/src/core/hle/service/bcat/news/news_service.cpp +++ b/src/core/hle/service/bcat/news/news_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h index 1a320df02f..5d6109243b 100644 --- a/src/core/hle/service/bcat/news/news_service.h +++ b/src/core/hle/service/bcat/news/news_service.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/bcat/news/news_storage.cpp b/src/core/hle/service/bcat/news/news_storage.cpp index 793c1fa94f..b65b680771 100644 --- a/src/core/hle/service/bcat/news/news_storage.cpp +++ b/src/core/hle/service/bcat/news/news_storage.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/bcat/news/news_storage.h" diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp index 1712971e4d..08449f1c4a 100644 --- a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.h b/src/core/hle/service/bcat/news/overwrite_event_holder.h index cdc87d782c..507f6ab668 100644 --- a/src/core/hle/service/bcat/news/overwrite_event_holder.h +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp index a1b22c0044..f27e4e52e6 100644 --- a/src/core/hle/service/bcat/news/service_creator.cpp +++ b/src/core/hle/service/bcat/news/service_creator.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/news/service_creator.h b/src/core/hle/service/bcat/news/service_creator.h index 5a62e7c1a9..42fbccd2a0 100644 --- a/src/core/hle/service/bcat/news/service_creator.h +++ b/src/core/hle/service/bcat/news/service_creator.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bcat/service_creator.h b/src/core/hle/service/bcat/service_creator.h index 50e6633248..2245007d6c 100644 --- a/src/core/hle/service/bcat/service_creator.h +++ b/src/core/hle/service/bcat/service_creator.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h index 524391ddb5..bf13ce5bae 100644 --- a/src/core/hle/service/bpc/bpc.h +++ b/src/core/hle/service/bpc/bpc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h index 42713860e6..4e739a96e3 100644 --- a/src/core/hle/service/btdrv/btdrv.h +++ b/src/core/hle/service/btdrv/btdrv.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 327c2d2ddb..b358387ffe 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index 0bf77d053e..902a61d8cc 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/btm/btm_debug.cpp b/src/core/hle/service/btm/btm_debug.cpp index 0c9a3a6557..abbe7d4832 100644 --- a/src/core/hle/service/btm/btm_debug.cpp +++ b/src/core/hle/service/btm/btm_debug.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/btm/btm_debug.h b/src/core/hle/service/btm/btm_debug.h index bf4f7e14fd..fad760d674 100644 --- a/src/core/hle/service/btm/btm_debug.h +++ b/src/core/hle/service/btm/btm_debug.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/btm/btm_system.h b/src/core/hle/service/btm/btm_system.h index fe1c6dbd78..9755540204 100644 --- a/src/core/hle/service/btm/btm_system.h +++ b/src/core/hle/service/btm/btm_system.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/btm/btm_user.h b/src/core/hle/service/btm/btm_user.h index d9ee5db455..d6cf330ed6 100644 --- a/src/core/hle/service/btm/btm_user.h +++ b/src/core/hle/service/btm/btm_user.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/btm/btm_user_core.h b/src/core/hle/service/btm/btm_user_core.h index dc0a22e810..5a565a5897 100644 --- a/src/core/hle/service/btm/btm_user_core.h +++ b/src/core/hle/service/btm/btm_user_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 58e9725b85..670ee59cda 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 1885ba6be0..c6aa8f9210 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index 0ecdfa1147..f50de0ee8c 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h index 179ae48409..b7ba2a642d 100644 --- a/src/core/hle/service/caps/caps_result.h +++ b/src/core/hle/service/caps/caps_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index 6117cb7c6c..92d53da9f5 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index d555f49792..72e947bf16 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index da4b4cc5fc..78971bde0e 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index 4b4cbd09e6..b7e53007ce 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index 023ee1fe79..debae97a46 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h index dad358b87c..783bce7781 100644 --- a/src/core/hle/service/cmif_types.h +++ b/src/core/hle/service/cmif_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/erpt/erpt.h b/src/core/hle/service/erpt/erpt.h index 60094f556e..456e9af910 100644 --- a/src/core/hle/service/erpt/erpt.h +++ b/src/core/hle/service/erpt/erpt.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/es/es.h b/src/core/hle/service/es/es.h index 3176806252..215aadb98f 100644 --- a/src/core/hle/service/es/es.h +++ b/src/core/hle/service/es/es.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 3cf27513ae..9b26ae9c83 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/eupld/eupld.h b/src/core/hle/service/eupld/eupld.h index 8eb0a5b4f2..851e286733 100644 --- a/src/core/hle/service/eupld/eupld.h +++ b/src/core/hle/service/eupld/eupld.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index f1c1104060..176ad8d0d9 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 4a81bb5e2f..345743d6bc 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index f743368350..ea2183c125 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 3739711fce..b2850c3e62 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 65fbe26961..4d193857d0 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h index 9d2465c0fb..89ef7096bb 100644 --- a/src/core/hle/service/fgm/fgm.h +++ b/src/core/hle/service/fgm/fgm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp index 8483394d0c..c4e3c81f22 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.h b/src/core/hle/service/filesystem/fsp/fs_i_directory.h index b6251f7fdb..79256604f3 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_directory.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.h b/src/core/hle/service/filesystem/fsp/fs_i_file.h index e8599ee2f8..14348f7fc6 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_file.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_file.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp b/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp index 6263282344..4af0a4ea2d 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.h b/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.h index 8ebf7c7d96..3ebeb2cb0a 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp index 495b131df4..0cac094caf 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h index e45ad852ba..d2b8eb97cf 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp index 50b7d1e765..8541c17817 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp @@ -4,6 +4,10 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #include "core/file_sys/errors.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/filesystem/fsp/fs_i_storage.h" @@ -31,6 +35,14 @@ Result IStorage::Read( R_UNLESS(length >= 0, FileSys::ResultInvalidSize); R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset); + const auto now = std::chrono::steady_clock::now(); + if (now < next_read_tick) { + std::this_thread::sleep_for(next_read_tick - now); + } + next_read_tick = std::chrono::steady_clock::now() + + std::chrono::nanoseconds{std::min(length, MaxChargedReadSize) * + 1'000'000'000 / ReadBytesPerSecond}; + // Read the data from the Storage backend backend->Read(out_bytes.data(), length, offset); diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.h b/src/core/hle/service/filesystem/fsp/fs_i_storage.h index 74d879386f..bac72d73c7 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_storage.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.h @@ -1,8 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include + #include "core/file_sys/vfs/vfs.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/filesystem/filesystem.h" @@ -17,6 +22,10 @@ public: private: FileSys::VirtualFile backend; + static constexpr s64 ReadBytesPerSecond = 40'000'000; + static constexpr s64 MaxChargedReadSize = 64 * 1024; + std::chrono::steady_clock::time_point next_read_tick{}; + Result Read( OutBuffer out_bytes, s64 offset, s64 length); diff --git a/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp index 8ee733f474..1dcacb0231 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fsp_ldr.h b/src/core/hle/service/filesystem/fsp/fsp_ldr.h index 358739a872..b642efe7ad 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp index 7c03ebaeae..e7941ef9c9 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fsp_pr.h b/src/core/hle/service/filesystem/fsp/fsp_pr.h index bd4e0a730c..175a3c7a23 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 7d270b7b6f..58303a0bd6 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_) {720, nullptr, "AbandonAccessFailure"}, {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, {810, nullptr, "RegisterProgramIndexMapInfo"}, + {820, nullptr, "GetContentStorageInfoIndex"}, + {830, nullptr, "EncryptStreamPlaySaveData"}, + {831, nullptr, "DecryptStreamPlaySaveData"}, {1000, nullptr, "SetBisRootForHost"}, {1001, nullptr, "SetSaveDataSize"}, {1002, nullptr, "SetSaveDataRootPath"}, diff --git a/src/core/hle/service/filesystem/fsp/fsp_types.h b/src/core/hle/service/filesystem/fsp/fsp_types.h index 294da6a2de..c704203ad1 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_types.h +++ b/src/core/hle/service/filesystem/fsp/fsp_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.cpp b/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.cpp index d8d6289fe1..f4c218db8d 100644 --- a/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.cpp +++ b/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h b/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h index d206e1dea4..6141bae18c 100644 --- a/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h +++ b/src/core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/romfs_controller.cpp b/src/core/hle/service/filesystem/romfs_controller.cpp index 19c9cec722..9725d02f4b 100644 --- a/src/core/hle/service/filesystem/romfs_controller.cpp +++ b/src/core/hle/service/filesystem/romfs_controller.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h index 3c3ead344c..5471e136c3 100644 --- a/src/core/hle/service/filesystem/romfs_controller.h +++ b/src/core/hle/service/filesystem/romfs_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp index 8720681d17..d3e22b1552 100644 --- a/src/core/hle/service/filesystem/save_data_controller.cpp +++ b/src/core/hle/service/filesystem/save_data_controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h index dc9d713dfb..afb95cb949 100644 --- a/src/core/hle/service/filesystem/save_data_controller.h +++ b/src/core/hle/service/filesystem/save_data_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index 2824dc7866..61ac92fbb4 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp index c8b98b1f0c..b436d43303 100644 --- a/src/core/hle/service/friend/friend_interface.cpp +++ b/src/core/hle/service/friend/friend_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/friend/friend_interface.h b/src/core/hle/service/friend/friend_interface.h index 3a2184c347..0315c62c39 100644 --- a/src/core/hle/service/friend/friend_interface.h +++ b/src/core/hle/service/friend/friend_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h index 5bce801755..ec3d809b5b 100644 --- a/src/core/hle/service/glue/arp.h +++ b/src/core/hle/service/glue/arp.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h index 5a5d9c9a7a..129193da1f 100644 --- a/src/core/hle/service/glue/bgtc.h +++ b/src/core/hle/service/glue/bgtc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h index 30feaa5c03..9839271447 100644 --- a/src/core/hle/service/glue/errors.h +++ b/src/core/hle/service/glue/errors.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h index 2a906f5ad6..64d45cfc14 100644 --- a/src/core/hle/service/glue/glue.h +++ b/src/core/hle/service/glue/glue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp index 22f0017044..78fc5ebac3 100644 --- a/src/core/hle/service/glue/glue_manager.cpp +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/glue_manager.h b/src/core/hle/service/glue/glue_manager.h index 216aa34c19..6330828834 100644 --- a/src/core/hle/service/glue/glue_manager.h +++ b/src/core/hle/service/glue/glue_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h index ef2522fdfb..7ae20ba6f8 100644 --- a/src/core/hle/service/glue/notif.h +++ b/src/core/hle/service/glue/notif.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h index 131d012a6a..c8882fed88 100644 --- a/src/core/hle/service/glue/time/alarm_worker.h +++ b/src/core/hle/service/glue/time/alarm_worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp index 048ff174c9..60bd688e72 100644 --- a/src/core/hle/service/glue/time/file_timestamp_worker.cpp +++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h index 5f8b9b0499..58f74e7d29 100644 --- a/src/core/hle/service/glue/time/file_timestamp_worker.h +++ b/src/core/hle/service/glue/time/file_timestamp_worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp index 0da790e1c8..0b3312904e 100644 --- a/src/core/hle/service/glue/time/manager.cpp +++ b/src/core/hle/service/glue/time/manager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h index 87e6a21fd5..5a86585754 100644 --- a/src/core/hle/service/glue/time/manager.h +++ b/src/core/hle/service/glue/time/manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp index 7470fb2259..0bc37f75da 100644 --- a/src/core/hle/service/glue/time/pm_state_change_handler.cpp +++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h index 27d9f78726..bf99fa8a14 100644 --- a/src/core/hle/service/glue/time/pm_state_change_handler.h +++ b/src/core/hle/service/glue/time/pm_state_change_handler.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp index 5ebaa33e00..e191405567 100644 --- a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h index 978d6b63b8..b5168cb837 100644 --- a/src/core/hle/service/glue/time/standard_steady_clock_resource.h +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp index d8672b7248..beccf6ce43 100644 --- a/src/core/hle/service/glue/time/static.cpp +++ b/src/core/hle/service/glue/time/static.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h index 7bb0e0b36e..a52b84bf31 100644 --- a/src/core/hle/service/glue/time/static.h +++ b/src/core/hle/service/glue/time/static.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp index 3cc1a62afd..eadad551e5 100644 --- a/src/core/hle/service/glue/time/time_zone_binary.cpp +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h index 135da631f2..012b933573 100644 --- a/src/core/hle/service/glue/time/time_zone_binary.h +++ b/src/core/hle/service/glue/time/time_zone_binary.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h index 69904e6748..f4be2ff367 100644 --- a/src/core/hle/service/glue/time/worker.h +++ b/src/core/hle/service/glue/time/worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/grc/grc.h b/src/core/hle/service/grc/grc.h index a3f8a5b90e..4c2b45057d 100644 --- a/src/core/hle/service/grc/grc.h +++ b/src/core/hle/service/grc/grc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/active_vibration_device_list.h b/src/core/hle/service/hid/active_vibration_device_list.h index beaa44d97a..f155eaf839 100644 --- a/src/core/hle/service/hid/active_vibration_device_list.h +++ b/src/core/hle/service/hid/active_vibration_device_list.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/hid/applet_resource.h b/src/core/hle/service/hid/applet_resource.h index d1e7db9b1e..40c3852baa 100644 --- a/src/core/hle/service/hid/applet_resource.h +++ b/src/core/hle/service/hid/applet_resource.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ec5463f4e2..bf8cd44481 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index 43fe0f3a19..42428a9ff9 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h index 3a483f07e5..f48f0205ed 100644 --- a/src/core/hle/service/hid/hid_debug_server.h +++ b/src/core/hle/service/hid/hid_debug_server.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index f3c01b5d89..e544adf03c 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr data; diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index 1a4f244d72..150b697305 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h index af4d956673..19dd894ed3 100644 --- a/src/core/hle/service/hid/hidbus.h +++ b/src/core/hle/service/hid/hidbus.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 58dfee6c36..2ca34b9106 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp index 75cc266ea9..2e41cbd3da 100644 --- a/src/core/hle/service/hid/xcd.cpp +++ b/src/core/hle/service/hid/xcd.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h index fe0b91b919..9000110a9a 100644 --- a/src/core/hle/service/hid/xcd.h +++ b/src/core/hle/service/hid/xcd.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h index 67eef7a087..a266b1a05c 100644 --- a/src/core/hle/service/ipc_helpers.h +++ b/src/core/hle/service/ipc_helpers.h @@ -22,6 +22,7 @@ namespace IPC { +constexpr Result ResultNotSupported{ErrorModule::HIPC, 1}; constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301}; struct ResponseBuilder { diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h index 19014c75a4..a9ebf146ec 100644 --- a/src/core/hle/service/jit/jit.h +++ b/src/core/hle/service/jit/jit.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 36bf5d6649..e1e8eeadbe 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -71,17 +71,14 @@ public: void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - u8 MemoryRead8(u64 vaddr) override { - return ReadMemory(vaddr); - } - u16 MemoryRead16(u64 vaddr) override { - return ReadMemory(vaddr); - } - u32 MemoryRead32(u64 vaddr) override { - return ReadMemory(vaddr); - } - u64 MemoryRead64(u64 vaddr) override { - return ReadMemory(vaddr); + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return ReadMemory(vaddr); + case sizeof(u32): return ReadMemory(vaddr); + case sizeof(u16): return ReadMemory(vaddr); + case sizeof(u8): return ReadMemory(vaddr); + default: UNREACHABLE(); + } } u128 MemoryRead128(u64 vaddr) override { return ReadMemory(vaddr); @@ -89,22 +86,19 @@ public: std::string MemoryReadCString(u64 vaddr) { std::string result{}; u8 next; - while ((next = MemoryRead8(vaddr++)) != 0) + while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0) result += char(next); return result; } - void MemoryWrite8(u64 vaddr, u8 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite16(u64 vaddr, u16 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite32(u64 vaddr, u32 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite64(u64 vaddr, u64 value) override { - WriteMemory(vaddr, value); + void MemoryWrite(u64 vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): WriteMemory(vaddr, u64(value)); break; + case sizeof(u32): WriteMemory(vaddr, u32(value)); break; + case sizeof(u16): WriteMemory(vaddr, u16(value)); break; + case sizeof(u8): WriteMemory(vaddr, u8(value)); break; + default: UNREACHABLE(); + } } void MemoryWrite128(u64 vaddr, u128 value) override { WriteMemory(vaddr, value); @@ -193,14 +187,14 @@ public: // The loaded NRO file has ELF relocations that must be processed before it can run. // Normally this would be processed by RTLD, but in HLE context, we don't have // the linker available, so we have to do it ourselves. - const VAddr mod_offset{callbacks->MemoryRead32(4)}; - if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) + const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))}; + if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0')) return false; // For more info about dynamic entries, see the ELF ABI specification: // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html - VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; + VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))}; VAddr rela_dyn = 0, relr_dyn = 0; size_t num_rela = 0, num_relr = 0; while (true) { @@ -222,8 +216,8 @@ public: for (size_t i = 0; i < num_rela; i++) { const auto rela{callbacks->ReadMemory(rela_dyn + i * sizeof(Elf64_Rela))}; if (Elf64RelType(rela.r_info) == ElfAArch64Relative) { - const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; - callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); + const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))}; + callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64)); } } @@ -231,7 +225,7 @@ public: for (size_t i = 0; i < num_relr; i++) { const auto relr = callbacks->ReadMemory(relr_dyn + i * sizeof(Elf64_Relr)); const auto incr = [&](VAddr where) { - callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); + callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64)); }; if ((relr & 1) == 0) { // where pointer @@ -294,7 +288,7 @@ public: if (argument_stack.size() > 8) { const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); for (size_t i = 8; i < argument_stack.size(); i++) - callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); + callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64)); jit->SetSP(new_sp); } // Reset the call state for the next invocation @@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) { if (dest < src) { for (size_t i = 0; i < n; i++) - MemoryWrite8(dest + i, MemoryRead8(src + i)); + MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8)); } else { for (size_t i = n; i > 0; i--) - MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); + MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8)); } } else if (pc == parent.helpers[size_t(HelperFn::Memset)]) { const VAddr dest{parent.jit->GetRegister(0)}; const u64 c{parent.jit->GetRegister(1)}; const size_t n{parent.jit->GetRegister(2)}; for (size_t i = 0; i < n; i++) - MemoryWrite8(dest + i, u8(c)); + MemoryWrite(dest + i, u8(c), sizeof(u8)); } else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) { // X0 contains a char* for a symbol to resolve const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; @@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) { } void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { - auto const inst = MemoryRead32(pc); + auto const inst = MemoryRead(pc, sizeof(u32)); LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst); parent.jit->HaltExecution(); } diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h index f17fc5e244..aa9a2d3589 100644 --- a/src/core/hle/service/jit/jit_context.h +++ b/src/core/hle/service/jit/jit_context.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h index e47759c01c..e2cd1b3980 100644 --- a/src/core/hle/service/lbl/lbl.h +++ b/src/core/hle/service/lbl/lbl.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp index f37dbe492f..90adcdcee4 100644 --- a/src/core/hle/service/ldn/lan_discovery.cpp +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -1,5 +1,6 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h index 5d0642bd0f..f1f3c4bece 100644 --- a/src/core/hle/service/ldn/lan_discovery.h +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h index dae037fa8f..76c7f4f439 100644 --- a/src/core/hle/service/ldn/ldn.h +++ b/src/core/hle/service/ldn/ldn.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h index f340bda422..cf6c22b54b 100644 --- a/src/core/hle/service/ldn/ldn_results.h +++ b/src/core/hle/service/ldn/ldn_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/monitor_service.cpp b/src/core/hle/service/ldn/monitor_service.cpp index ea6ac4d5dd..6cb7180b15 100644 --- a/src/core/hle/service/ldn/monitor_service.cpp +++ b/src/core/hle/service/ldn/monitor_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/monitor_service.h b/src/core/hle/service/ldn/monitor_service.h index e663145b4f..2d4a69c311 100644 --- a/src/core/hle/service/ldn/monitor_service.h +++ b/src/core/hle/service/ldn/monitor_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_monitor_service.cpp b/src/core/hle/service/ldn/sf_monitor_service.cpp index 9e6736ff2c..5ac663b4c2 100644 --- a/src/core/hle/service/ldn/sf_monitor_service.cpp +++ b/src/core/hle/service/ldn/sf_monitor_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_monitor_service.h b/src/core/hle/service/ldn/sf_monitor_service.h index d021152010..a596093f1b 100644 --- a/src/core/hle/service/ldn/sf_monitor_service.h +++ b/src/core/hle/service/ldn/sf_monitor_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_service.cpp b/src/core/hle/service/ldn/sf_service.cpp index 61cabe219a..b77672a1b6 100644 --- a/src/core/hle/service/ldn/sf_service.cpp +++ b/src/core/hle/service/ldn/sf_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_service.h b/src/core/hle/service/ldn/sf_service.h index 05534b5677..ca7a81cf29 100644 --- a/src/core/hle/service/ldn/sf_service.h +++ b/src/core/hle/service/ldn/sf_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_service_monitor.cpp b/src/core/hle/service/ldn/sf_service_monitor.cpp index 9a1cde6881..95a9bcaf71 100644 --- a/src/core/hle/service/ldn/sf_service_monitor.cpp +++ b/src/core/hle/service/ldn/sf_service_monitor.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/sf_service_monitor.h b/src/core/hle/service/ldn/sf_service_monitor.h index 924cc17594..87854b868d 100644 --- a/src/core/hle/service/ldn/sf_service_monitor.h +++ b/src/core/hle/service/ldn/sf_service_monitor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/system_local_communication_service.cpp b/src/core/hle/service/ldn/system_local_communication_service.cpp index 7b52223cdc..57a1c6a6c2 100644 --- a/src/core/hle/service/ldn/system_local_communication_service.cpp +++ b/src/core/hle/service/ldn/system_local_communication_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/system_local_communication_service.h b/src/core/hle/service/ldn/system_local_communication_service.h index a02b097ea7..fa0e45bde4 100644 --- a/src/core/hle/service/ldn/system_local_communication_service.h +++ b/src/core/hle/service/ldn/system_local_communication_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/user_local_communication_service.cpp b/src/core/hle/service/ldn/user_local_communication_service.cpp index 572aea6314..7011bd52f2 100644 --- a/src/core/hle/service/ldn/user_local_communication_service.cpp +++ b/src/core/hle/service/ldn/user_local_communication_service.cpp @@ -1,5 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ldn/user_local_communication_service.h b/src/core/hle/service/ldn/user_local_communication_service.h index 1e725ed531..720d5e9bf4 100644 --- a/src/core/hle/service/ldn/user_local_communication_service.h +++ b/src/core/hle/service/ldn/user_local_communication_service.h @@ -1,7 +1,7 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h index c9281dbfb2..12a6b1f507 100644 --- a/src/core/hle/service/ldr/ldr.h +++ b/src/core/hle/service/ldr/ldr.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 0d7c39cbc3..890344f44a 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index edc96506c0..7412674417 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h index c8ed732a5b..39e92a59eb 100644 --- a/src/core/hle/service/mig/mig.h +++ b/src/core/hle/service/mig/mig.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h index 8683ac1a52..9ec6f248e5 100644 --- a/src/core/hle/service/mii/mii.h +++ b/src/core/hle/service/mii/mii.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii_database.cpp b/src/core/hle/service/mii/mii_database.cpp index d1f4e1f198..6dd9773b2b 100644 --- a/src/core/hle/service/mii/mii_database.cpp +++ b/src/core/hle/service/mii/mii_database.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project diff --git a/src/core/hle/service/mii/mii_database.h b/src/core/hle/service/mii/mii_database.h index 3bd240f93b..6d28696206 100644 --- a/src/core/hle/service/mii/mii_database.h +++ b/src/core/hle/service/mii/mii_database.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii_database_manager.h b/src/core/hle/service/mii/mii_database_manager.h index 52c32be82c..52ec3a91cc 100644 --- a/src/core/hle/service/mii/mii_database_manager.h +++ b/src/core/hle/service/mii/mii_database_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index 48d8e8bb7d..9c546d2cbf 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h index e2c36e556d..f4964133e7 100644 --- a/src/core/hle/service/mii/mii_result.h +++ b/src/core/hle/service/mii/mii_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h index 08c6029dff..6afa07ef43 100644 --- a/src/core/hle/service/mii/mii_types.h +++ b/src/core/hle/service/mii/mii_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp index e90124af4c..b5f4492994 100644 --- a/src/core/hle/service/mii/types/char_info.cpp +++ b/src/core/hle/service/mii/types/char_info.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h index d0c457fd56..e9c41435e1 100644 --- a/src/core/hle/service/mii/types/char_info.h +++ b/src/core/hle/service/mii/types/char_info.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp index ba1da76bae..7f2acea3ef 100644 --- a/src/core/hle/service/mii/types/core_data.cpp +++ b/src/core/hle/service/mii/types/core_data.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h index 8897e4f3b3..48a1ba5c73 100644 --- a/src/core/hle/service/mii/types/core_data.h +++ b/src/core/hle/service/mii/types/core_data.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h index 9a4cfa738e..f8182847ec 100644 --- a/src/core/hle/service/mii/types/raw_data.h +++ b/src/core/hle/service/mii/types/raw_data.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp index 127221fdb6..fcef3850f2 100644 --- a/src/core/hle/service/mii/types/store_data.cpp +++ b/src/core/hle/service/mii/types/store_data.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h index ed5dfb9494..5d5269a15b 100644 --- a/src/core/hle/service/mii/types/store_data.h +++ b/src/core/hle/service/mii/types/store_data.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index c27646fcfd..840e0f35b3 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h index 47907bf7d7..f2cb395136 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.h +++ b/src/core/hle/service/mii/types/ver3_store_data.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h index 43117c9b1c..ab0e76f40a 100644 --- a/src/core/hle/service/mm/mm_u.h +++ b/src/core/hle/service/mm/mm_u.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h index b78efdcd79..1d8bc7a9ea 100644 --- a/src/core/hle/service/ncm/ncm.h +++ b/src/core/hle/service/ncm/ncm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index d59202d18f..98063bbc29 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index 6c0e6b255e..116ae8cb7e 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h index 16a9171e60..399f01313e 100644 --- a/src/core/hle/service/nfc/mifare_result.h +++ b/src/core/hle/service/nfc/mifare_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h index 4679373994..237f60f0de 100644 --- a/src/core/hle/service/nfc/mifare_types.h +++ b/src/core/hle/service/nfc/mifare_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index d15955b755..bfda30d6f1 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfc/nfc_interface.h b/src/core/hle/service/nfc/nfc_interface.h index 5cc0d8ec04..a1e0d8d9ca 100644 --- a/src/core/hle/service/nfc/nfc_interface.h +++ b/src/core/hle/service/nfc/nfc_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 464b5fd69a..aab83c326c 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h index 6d44014ea1..6774a9b4d6 100644 --- a/src/core/hle/service/nfc/nfc_types.h +++ b/src/core/hle/service/nfc/nfc_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index a5aac710b5..e7af7ae39d 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfp/nfp_interface.h b/src/core/hle/service/nfp/nfp_interface.h index fa985b068e..531db84652 100644 --- a/src/core/hle/service/nfp/nfp_interface.h +++ b/src/core/hle/service/nfp/nfp_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index 6185338438..528b5417fa 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 8330134d18..108548ea46 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project diff --git a/src/core/hle/service/ngc/ngc.cpp b/src/core/hle/service/ngc/ngc.cpp index 06310145e5..e79108a1eb 100644 --- a/src/core/hle/service/ngc/ngc.cpp +++ b/src/core/hle/service/ngc/ngc.cpp @@ -6,10 +6,16 @@ #include "common/string_util.h" #include "core/core.h" +#include "core/hle/kernel/k_client_session.h" +#include "core/hle/result.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ngc/ngc.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" +#include "frontend_common/firmware_manager.h" namespace Service::NGC { @@ -166,12 +172,123 @@ public: } }; +struct SaveDataHandle { + u64 unk0; +}; +static_assert(sizeof(SaveDataHandle) == 0x08); + +class IUserShimScopedObject final : public ServiceFramework { +public: + explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") { + // clang-format off + static const FunctionInfo functions[] = { + {450, nullptr, "InitializeForSaveData"}, + {451, nullptr, "FinalizeForSaveData"}, + {452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"}, + {453, nullptr, "CloseSaveData"}, + {454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"}, + {455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"}, + {456, nullptr, "FlushSaveSlot"}, + {457, nullptr, "CommitSaveData"}, + }; + // clang-format on + RegisterHandlers(functions); + } + + Result OpenSaveData(Account::Uid unk0, Out unk1) { + LOG_WARNING(Service_NGC, "stubbed"); + R_THROW(IPC::ResultNotSupported); + } + + Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer out_data, Out out_size) { + LOG_WARNING(Service_NGC, "stubbed"); + R_THROW(IPC::ResultNotSupported); + } + + Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer out_data) { + LOG_WARNING(Service_NGC, "stubbed"); + // to implement + R_SUCCEED(); + } +}; + +class IUserService final : public ServiceFramework { +public: + explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") { + // clang-format off + static const FunctionInfo functions[] = { + {0 , D<&IUserService::Cmd0>, "Cmd0"}, + }; + // clang-format on + RegisterHandlers(functions); + } + Result Cmd0(u32 unk0, OutInterface out_interface) { + LOG_WARNING(Service_NGC, "stubbed"); + *out_interface = std::make_shared(system); + R_SUCCEED(); + } +}; + +class ISystemShimScopedObject final : public ServiceFramework { +public: + explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") { + // clang-format off + static const FunctionInfo functions[] = { + {106, nullptr, "Cmd106"}, + {107, nullptr, "Cmd107"}, + {108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"}, + {207, nullptr, "Cmd207"}, + {208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"}, + {209, nullptr, "Cmd209"}, + {210, nullptr, "Cmd210"}, + {211, nullptr, "Cmd211"}, + {212, nullptr, "Cmd212"}, + }; + // clang-format on + RegisterHandlers(functions); + } + + Result Cmd108() { + LOG_WARNING(Service_NGC, "stubbed"); + R_THROW(IPC::ResultNotSupported); + } + + Result Cmd208(Out> unk0) { + LOG_WARNING(Service_NGC, "stubbed"); + R_THROW(IPC::ResultNotSupported); + } +}; + +class ISystemService final : public ServiceFramework { +public: + explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") { + // clang-format off + static const FunctionInfo functions[] = { + {0 , D<&ISystemService::Cmd0>, "Cmd0"}, + }; + // clang-format on + RegisterHandlers(functions); + } + Result Cmd0(OutInterface out_interface) { + LOG_WARNING(Service_NGC, "stubbed"); + *out_interface = std::make_shared(system); + R_SUCCEED(); + } +}; + void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService("ngct:u", std::make_shared(system), 4); server_manager->RegisterNamedService("ngct:s", std::make_shared(system), 4); server_manager->RegisterNamedService("ngc:u", std::make_shared(system), 4); + + // +23.0.0 + if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) { + server_manager->RegisterNamedService("stpl:u", std::make_shared(system), 4); + server_manager->RegisterNamedService("stpl:sys", std::make_shared(system), 4); + } + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/ngc/ngc.h b/src/core/hle/service/ngc/ngc.h index 823b1aa819..7dc4a46577 100644 --- a/src/core/hle/service/ngc/ngc.h +++ b/src/core/hle/service/ngc/ngc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 3b5a438c75..44859e0c51 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index e7d599908d..0fad2a71af 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h index 0019fca763..e54a91a213 100644 --- a/src/core/hle/service/npns/npns.h +++ b/src/core/hle/service/npns/npns.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/account_proxy_interface.cpp b/src/core/hle/service/ns/account_proxy_interface.cpp index e5041af66b..2e3dcf928d 100644 --- a/src/core/hle/service/ns/account_proxy_interface.cpp +++ b/src/core/hle/service/ns/account_proxy_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/account_proxy_interface.h b/src/core/hle/service/ns/account_proxy_interface.h index e944d2a75f..14cec4ddbc 100644 --- a/src/core/hle/service/ns/account_proxy_interface.h +++ b/src/core/hle/service/ns/account_proxy_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h index af5ec780af..c4ca66f319 100644 --- a/src/core/hle/service/ns/application_manager_interface.h +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -7,7 +7,7 @@ #pragma once #include "core/hle/service/cmif_types.h" -#include "core/hle/service/ns/i_async_result.h" +#include "core/hle/service/ns/async_result.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns_types.h" #include "core/hle/service/os/event.h" diff --git a/src/core/hle/service/ns/application_version_interface.cpp b/src/core/hle/service/ns/application_version_interface.cpp index b89e127dbe..0ed9514c2c 100644 --- a/src/core/hle/service/ns/application_version_interface.cpp +++ b/src/core/hle/service/ns/application_version_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/application_version_interface.h b/src/core/hle/service/ns/application_version_interface.h index b288cff1be..fcbb966a4d 100644 --- a/src/core/hle/service/ns/application_version_interface.h +++ b/src/core/hle/service/ns/application_version_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/i_async_result.cpp b/src/core/hle/service/ns/async_result.cpp similarity index 91% rename from src/core/hle/service/ns/i_async_result.cpp rename to src/core/hle/service/ns/async_result.cpp index be04c162d3..8617280bef 100644 --- a/src/core/hle/service/ns/i_async_result.cpp +++ b/src/core/hle/service/ns/async_result.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/cmif_serialization.h" -#include "core/hle/service/ns/i_async_result.h" +#include "core/hle/service/ns/async_result.h" #include @@ -32,4 +32,4 @@ Result IAsyncResult::Cancel() { R_SUCCEED(); } -} // namespace Service::NS \ No newline at end of file +} // namespace Service::NS diff --git a/src/core/hle/service/ns/i_async_result.h b/src/core/hle/service/ns/async_result.h similarity index 100% rename from src/core/hle/service/ns/i_async_result.h rename to src/core/hle/service/ns/async_result.h diff --git a/src/core/hle/service/ns/develop_interface.cpp b/src/core/hle/service/ns/develop_interface.cpp index 880bdbebbf..5a44c61f1b 100644 --- a/src/core/hle/service/ns/develop_interface.cpp +++ b/src/core/hle/service/ns/develop_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/develop_interface.h b/src/core/hle/service/ns/develop_interface.h index a9f81ccd60..d7cb9e7a56 100644 --- a/src/core/hle/service/ns/develop_interface.h +++ b/src/core/hle/service/ns/develop_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/document_interface.cpp b/src/core/hle/service/ns/document_interface.cpp index 51a1e46c0d..00593d1edc 100644 --- a/src/core/hle/service/ns/document_interface.cpp +++ b/src/core/hle/service/ns/document_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/document_interface.h b/src/core/hle/service/ns/document_interface.h index cd461652cf..4c8b15b54a 100644 --- a/src/core/hle/service/ns/document_interface.h +++ b/src/core/hle/service/ns/document_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/download_task_interface.cpp b/src/core/hle/service/ns/download_task_interface.cpp index 62dc7f1874..18db60e7b0 100644 --- a/src/core/hle/service/ns/download_task_interface.cpp +++ b/src/core/hle/service/ns/download_task_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/download_task_interface.h b/src/core/hle/service/ns/download_task_interface.h index b1cb69cb86..1c46339daa 100644 --- a/src/core/hle/service/ns/download_task_interface.h +++ b/src/core/hle/service/ns/download_task_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/ecommerce_interface.cpp b/src/core/hle/service/ns/ecommerce_interface.cpp index 76fc425f0c..b8794bb5fb 100644 --- a/src/core/hle/service/ns/ecommerce_interface.cpp +++ b/src/core/hle/service/ns/ecommerce_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/ecommerce_interface.h b/src/core/hle/service/ns/ecommerce_interface.h index 4352101f4a..6ff9512123 100644 --- a/src/core/hle/service/ns/ecommerce_interface.h +++ b/src/core/hle/service/ns/ecommerce_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/factory_reset_interface.cpp b/src/core/hle/service/ns/factory_reset_interface.cpp index fd5cf7e1fd..11cdefb4be 100644 --- a/src/core/hle/service/ns/factory_reset_interface.cpp +++ b/src/core/hle/service/ns/factory_reset_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/factory_reset_interface.h b/src/core/hle/service/ns/factory_reset_interface.h index 50d1251239..8181d6b379 100644 --- a/src/core/hle/service/ns/factory_reset_interface.h +++ b/src/core/hle/service/ns/factory_reset_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index f79b4ae3d6..2b14d115ca 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/ns_results.h b/src/core/hle/service/ns/ns_results.h index 16d2ea6f7b..52f4c0ea59 100644 --- a/src/core/hle/service/ns/ns_results.h +++ b/src/core/hle/service/ns/ns_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/platform_service_manager.h b/src/core/hle/service/ns/platform_service_manager.h index b82c385a6c..73521abbc2 100644 --- a/src/core/hle/service/ns/platform_service_manager.h +++ b/src/core/hle/service/ns/platform_service_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index baf28b9b5c..1359cdbb95 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -311,27 +311,30 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon // u64 - app count memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64)); out_length += sizeof(u64); + ASSERT(out_length <= t_mem->GetSize()); // [list of u64] - size of icons for (size_t i = 0; i < app_count; ++i) { const u64 app_id = app_ids_buffer[i]; const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - u64 full_size = control.second->GetSize(); - memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64)); + if (const auto control = pm.GetControlMetadata(); control.second) { + u64 full_size = control.second->GetSize(); + memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64)); + } out_length += sizeof(u64); + ASSERT(out_length <= t_mem->GetSize()); } // [list of raw icon data] - std::vector full_icon_data; for (size_t i = 0; i < app_count; ++i) { const u64 app_id = app_ids_buffer[i]; const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - auto const full_size = control.second->GetSize(); - if (full_size > 0) { - full_icon_data.resize(full_size); - control.second->Read(full_icon_data.data(), full_size, 0); - memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size); - out_length += full_size; + if (const auto control = pm.GetControlMetadata(); control.second) { + if (auto const full_size = control.second->GetSize(); full_size > 0) { + std::vector full_icon_data(full_size); + control.second->Read(full_icon_data.data(), full_size, 0); + memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size); + out_length += full_size; + ASSERT(out_length <= t_mem->GetSize()); + } } } } @@ -345,6 +348,12 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) { const auto app_ids_buffer = ctx.ReadBuffer(); const size_t app_count = app_ids_buffer.size() / sizeof(u64); + + std::vector application_ids(app_count); + if (app_count > 0) { + std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64)); + } + auto t_mem_obj = ctx.GetObjectFromHandle(ctx.GetCopyHandle(0)); auto* t_mem = t_mem_obj.GetPointerUnsafe(); constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry); @@ -354,8 +363,9 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo auto& memory = system.ApplicationMemory(); const auto t_mem_address = t_mem->GetSourceAddress(); for (size_t i = 0; i < app_count; ++i) { - const u64 app_id = app_ids_buffer[i]; - const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; + const u64 app_id = application_ids[i]; + const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), + system.GetContentProvider()}; const auto control = pm.GetControlMetadata(); FileSys::LanguageEntry entry{}; if (control.first != nullptr) { diff --git a/src/core/hle/service/ns/read_only_application_record_interface.h b/src/core/hle/service/ns/read_only_application_record_interface.h index fc43c768ba..dd842070e8 100644 --- a/src/core/hle/service/ns/read_only_application_record_interface.h +++ b/src/core/hle/service/ns/read_only_application_record_interface.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/ns/service_getter_interface.cpp b/src/core/hle/service/ns/service_getter_interface.cpp index 1a3dd7166c..7b7f791650 100644 --- a/src/core/hle/service/ns/service_getter_interface.cpp +++ b/src/core/hle/service/ns/service_getter_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/service_getter_interface.h b/src/core/hle/service/ns/service_getter_interface.h index bbc18d4445..fc36b04325 100644 --- a/src/core/hle/service/ns/service_getter_interface.h +++ b/src/core/hle/service/ns/service_getter_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/system_update_control.cpp b/src/core/hle/service/ns/system_update_control.cpp index f5f5cfd909..2711db508c 100644 --- a/src/core/hle/service/ns/system_update_control.cpp +++ b/src/core/hle/service/ns/system_update_control.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/system_update_control.h b/src/core/hle/service/ns/system_update_control.h index a30a090000..70794f4abd 100644 --- a/src/core/hle/service/ns/system_update_control.h +++ b/src/core/hle/service/ns/system_update_control.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/system_update_interface.cpp b/src/core/hle/service/ns/system_update_interface.cpp index 7e22ca3db0..3f4d42fdb6 100644 --- a/src/core/hle/service/ns/system_update_interface.cpp +++ b/src/core/hle/service/ns/system_update_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ns/system_update_interface.h b/src/core/hle/service/ns/system_update_interface.h index 36a2880ec9..76f8959ed6 100644 --- a/src/core/hle/service/ns/system_update_interface.h +++ b/src/core/hle/service/ns/system_update_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index b2cbfbd686..e2bf49da62 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -211,19 +211,14 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { while ((address = smmu.Allocate(aligned_up)) == 0) { // Free handles until the allocation succeeds std::scoped_lock queueLock(unmap_queue_lock); - if (unmap_queue.empty()) { - LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); - return 0; - } - // Handles in the unmap queue are guaranteed not to be pinned so don't bother - // checking if they are before unmapping - const std::shared_ptr freeHandleDesc = unmap_queue.front(); - std::scoped_lock freeLock(freeHandleDesc->mutex); - if (freeHandleDesc->d_address) { - UnmapHandle(*freeHandleDesc); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->d_address) + UnmapHandle(*freeHandleDesc); } else { - unmap_queue.pop_front(); - freeHandleDesc->unmap_queue_entry.reset(); + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); } } diff --git a/src/core/hle/service/nvdrv/devices/ioctl_serialization.h b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h index c3baa3a75b..37d04e3cf1 100644 --- a/src/core/hle/service/nvdrv/devices/ioctl_serialization.h +++ b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 8adaddc60b..d23ec63bae 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index f486817c8d..3c9e42f602 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -233,9 +233,9 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { if (allocation.page_size != params.page_size || allocation.size != (u64(params.pages) * params.page_size)) return NvResult::BadValue; - for (const auto mapping_offset : allocation.mappings) { - void(FreeMappingLocked(mapping_offset)); - } + for (const auto mapping_offset : allocation.mappings) + if (!FreeMappingLocked(mapping_offset)) + return NvResult::BadValue; // Unset sparse flag if required if (allocation.sparse) @@ -375,16 +375,39 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { mapping_map.insert_or_assign(params.offset, Mapping(params.handle, device_address, params.offset, size, false, big_page, false)); } + map_buffer_offsets.insert(params.offset); + return NvResult::Success; } NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { - LOG_DEBUG(Service_NVDRV, "called, offset={:#x}", params.offset); std::scoped_lock lock(mutex); - if (!vm.initialised) { - return NvResult::BadValue; + if (auto const offset_it = map_buffer_offsets.find(params.offset); offset_it != map_buffer_offsets.end()) { + LOG_DEBUG(Service_NVDRV, "called, offset={:#x}", params.offset); + if (!vm.initialised) { + return NvResult::BadValue; + } + + auto const it = mapping_map.find(params.offset); + auto const mapping = it->second; + if (!mapping.fixed) { + auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; + u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; + allocator.Free(u32(mapping.offset >> page_size_bits), u32(mapping.size >> page_size_bits)); + } + + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state + // Only FreeSpace can unmap them fully + if (mapping.sparse_alloc) { + gmmu->MapSparse(params.offset, mapping.size, mapping.big_page); + } else { + gmmu->Unmap(params.offset, mapping.size); + } + + nvmap.UnpinHandle(mapping.handle); + mapping_map.erase(params.offset); + map_buffer_offsets.erase(params.offset); } - void(FreeMappingLocked(params.offset)); return NvResult::Success; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f66dcf0dc2..b13b1c9efa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -113,6 +113,8 @@ private: }; static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); + ::Common::unordered_set map_buffer_offsets{}; + struct IoctlMapBufferEx { MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable u32_le kind{}; // -1 is default diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 7db706c5e7..6ef32b890f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -13,7 +13,6 @@ #include "common/assert.h" #include "common/logging.h" #include "common/scope_exit.h" -#include "common/settings.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/nvdrv/core/container.h" @@ -148,12 +147,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_a const auto check_failing = [&]() { if (events[slot].fails > 2) { - std::unique_lock stall; - if (Settings::values.stall_on_gpu_fence_wait.GetValue()) { - stall = system.StallApplication(); - } - host1x_syncpoint_manager.WaitHost(fence_id, target_value); - if (stall.owns_lock()) { + { + auto lk = system.StallApplication(); + host1x_syncpoint_manager.WaitHost(fence_id, target_value); system.UnstallApplication(); } params.value.raw = target_value; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 193b28573e..1e571be4c9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -278,7 +278,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { params.flags = allowed_mask; } - s32_le ctx_class_number_index = + s32_le ctx_class_number_index = GetObjectContextClassNumberIndex(static_cast(params.class_num)); if (ctx_class_number_index < 0) { LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#x}", @@ -312,17 +312,19 @@ static boost::container::small_vector BuildWaitComman static boost::container::small_vector BuildIncrementCommandList( NvFence fence) { - const Tegra::CommandHeader increment = - BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id); - return { + boost::container::small_vector result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), - {}, - Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 2, - Tegra::SubmissionMode::NonIncreasing), - increment, - increment, - }; + {}}; + + for (u32 count = 0; count < 2; ++count) { + result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, + Tegra::SubmissionMode::Increasing)); + result.push_back( + BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); + } + + return result; } static boost::container::small_vector BuildIncrementWithWfiCommandList( @@ -368,14 +370,17 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); + gpu.PushGPUEntries(bind_id, std::move(entries)); + if (flags.fence_increment.Value()) { if (flags.suppress_wfi.Value()) { - entries.prefetch_command_list = BuildIncrementCommandList(params.fence); + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementCommandList(params.fence)}); } else { - entries.prefetch_command_list = BuildIncrementWithWfiCommandList(params.fence); + gpu.PushGPUEntries(bind_id, + Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); } } - gpu.PushGPUEntries(bind_id, std::move(entries)); flags.raw = 0; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f47369adb8..7ac3dfaa46 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -8,7 +8,6 @@ #include "common/assert.h" #include "common/logging.h" #include "core/core.h" -#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" @@ -72,23 +71,17 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span in void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream started"); + system.SetNVDECActive(true); sessions[fd] = session_id; - if (const auto* session = core.GetSession(session_id); - session != nullptr && session->process != nullptr) { - system.NotifyNVDECChannelOpen(session->process->GetId()); - } host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint); } void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec); + system.SetNVDECActive(false); auto it = sessions.find(fd); if (it != sessions.end()) { - if (const auto* session = core.GetSession(it->second); - session != nullptr && session->process != nullptr) { - system.NotifyNVDECChannelClose(session->process->GetId()); - } sessions.erase(it); } } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 627686757b..1747881d48 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index def9c254d1..e0735b7f01 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 0cc04354a0..b1cbcb9ad7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index dd82e76d9e..b252a2efa5 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -165,22 +165,13 @@ Module::Module(Core::System& system) Module::~Module() {} -std::shared_ptr Module::FindDevice(DeviceFD fd) const { - std::scoped_lock lock(open_files_mutex); - const auto itr = open_files.find(fd); - if (itr == open_files.end()) { - return nullptr; - } - return itr->second; -} - NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } - if (!FindDevice(fd)) { + if (open_files.find(fd) == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } @@ -196,11 +187,8 @@ DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_ } const DeviceFD fd = next_fd++; - std::shared_ptr device; - { - std::scoped_lock lock(open_files_mutex); - device = it->second(fd)->second; - } + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(session_id, fd); @@ -214,13 +202,14 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span input, return NvResult::InvalidState; } - const auto device = FindDevice(fd); - if (!device) { + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } - return device->Ioctl1(fd, command, input, output); + return itr->second->Ioctl1(fd, command, input, output); } NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span input, @@ -230,13 +219,14 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span input, return NvResult::InvalidState; } - const auto device = FindDevice(fd); - if (!device) { + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } - return device->Ioctl2(fd, command, input, inline_input, output); + return itr->second->Ioctl2(fd, command, input, inline_input, output); } NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span input, std::span output, @@ -246,13 +236,14 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span input, s return NvResult::InvalidState; } - const auto device = FindDevice(fd); - if (!device) { + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } - return device->Ioctl3(fd, command, input, output, inline_output); + return itr->second->Ioctl3(fd, command, input, output, inline_output); } NvResult Module::Close(DeviceFD fd) { @@ -261,17 +252,16 @@ NvResult Module::Close(DeviceFD fd) { return NvResult::InvalidState; } - const auto device = FindDevice(fd); - if (!device) { + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } - { - std::scoped_lock lock(open_files_mutex); - open_files.erase(fd); - } - device->OnClose(fd); + itr->second->OnClose(fd); + + open_files.erase(itr); return NvResult::Success; } @@ -282,13 +272,14 @@ NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { return NvResult::InvalidState; } - const auto device = FindDevice(fd); - if (!device) { + const auto itr = open_files.find(fd); + + if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } - event = device->QueryEvent(event_id); + event = itr->second->QueryEvent(event_id); if (!event) { return NvResult::BadParameter; } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 4d8873c28f..f503f108c3 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -65,7 +65,10 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template std::shared_ptr GetDevice(DeviceFD fd) { - return std::static_pointer_cast(FindDevice(fd)); + auto itr = open_files.find(fd); + if (itr == open_files.end()) + return nullptr; + return std::static_pointer_cast(itr->second); } NvResult VerifyFD(DeviceFD fd) const; @@ -94,8 +97,6 @@ public: private: friend class EventInterface; - std::shared_ptr FindDevice(DeviceFD fd) const; - /// Manages syncpoints on the host NvCore::Container container; @@ -105,7 +106,6 @@ private: using FilesContainerType = ::Common::unordered_map>; /// Mapping of file descriptors to the devices they reference. FilesContainerType open_files; - mutable std::mutex open_files_mutex; KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index c72f925979..f628fca7df 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h index 85e3053a8d..6ff59834fe 100644 --- a/src/core/hle/service/nvdrv/nvmemp.h +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvnflinger/binder.h b/src/core/hle/service/nvnflinger/binder.h index 124accb946..6c7ce0ed0f 100644 --- a/src/core/hle/service/nvnflinger/binder.h +++ b/src/core/hle/service/nvnflinger/binder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h index 7fd808f546..718188d849 100644 --- a/src/core/hle/service/nvnflinger/buffer_item.h +++ b/src/core/hle/service/nvnflinger/buffer_item.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.h b/src/core/hle/service/nvnflinger/buffer_item_consumer.h index 9f95c92800..7857569c0f 100644 --- a/src/core/hle/service/nvnflinger/buffer_item_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h index a9226f1c35..84f36285c0 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_queue_defs.h b/src/core/hle/service/nvnflinger/buffer_queue_defs.h index 6fd3156f4a..77ebd0a6a0 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_defs.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_defs.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/buffer_transform_flags.h b/src/core/hle/service/nvnflinger/buffer_transform_flags.h index ffe579718f..6fb40d0a72 100644 --- a/src/core/hle/service/nvnflinger/buffer_transform_flags.h +++ b/src/core/hle/service/nvnflinger/buffer_transform_flags.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h index b29c16f86b..e187b520ca 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.h +++ b/src/core/hle/service/nvnflinger/consumer_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/consumer_listener.h b/src/core/hle/service/nvnflinger/consumer_listener.h index 74a1939883..4b4d939551 100644 --- a/src/core/hle/service/nvnflinger/consumer_listener.h +++ b/src/core/hle/service/nvnflinger/consumer_listener.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp index d72b49a8e7..5de604233d 100644 --- a/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp +++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h index 5d7cff7d38..05980eb482 100644 --- a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h +++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index 5decb791e1..9ac56e17bb 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -57,8 +57,21 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, // Set default speed limit to 100%. *out_speed_scale = 1.0f; + // If no layers are visible, skip the logic. Acquiring and holding buffers here would + // consume the producer's queue without ever presenting. + bool any_visible = false; + for (auto& layer : display.stack.layers) { + if (layer->visible) { + any_visible = true; + break; + } + } + if (!any_visible) { + return 1; + } + + // Wait for the previous composite, bounding the number of in-flight composites. nvdisp.WaitForComposite(); - this->ReleaseFramebuffersLocked(display); // Determine the number of vsync periods to wait before composing again. std::optional swap_interval{}; @@ -152,6 +165,13 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, // Advance by 1 frame (60 FPS compositing) m_frame_number += 1; + // Release framebuffers once the frame has been handed to the compositor. This happens + // within the same vsync period as the acquire (release_frame_number is checked against + // the advanced counter), so the producer gets its buffers back immediately instead of + // losing a queue slot for a whole frame. + // TODO: support release fences; releasing before the composite has finished can tear. + this->ReleaseFramebuffersLocked(display); + return 1; } diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp index d30aa3717d..fa1becc34e 100644 --- a/src/core/hle/service/nvnflinger/hos_binder_driver.cpp +++ b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 9e3b68b8a0..48862a747d 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 5c41f3013c..2f5465e7ef 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h index 4567942016..ed85512792 100644 --- a/src/core/hle/service/nvnflinger/parcel.h +++ b/src/core/hle/service/nvnflinger/parcel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project diff --git a/src/core/hle/service/nvnflinger/pixel_format.h b/src/core/hle/service/nvnflinger/pixel_format.h index f77d0acfb3..b6e8b5f8a0 100644 --- a/src/core/hle/service/nvnflinger/pixel_format.h +++ b/src/core/hle/service/nvnflinger/pixel_format.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/producer_listener.h b/src/core/hle/service/nvnflinger/producer_listener.h index 6bf8aaf1ee..892f6d531d 100644 --- a/src/core/hle/service/nvnflinger/producer_listener.h +++ b/src/core/hle/service/nvnflinger/producer_listener.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h index 3fa0fe15b9..7b188b42f5 100644 --- a/src/core/hle/service/nvnflinger/status.h +++ b/src/core/hle/service/nvnflinger/status.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp index ede2a11933..5ba7a31cc8 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h index da430aa752..099429b3f0 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/nvnflinger/window.h b/src/core/hle/service/nvnflinger/window.h index 36d6cde3db..78a99f57ae 100644 --- a/src/core/hle/service/nvnflinger/window.h +++ b/src/core/hle/service/nvnflinger/window.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h index 620b634fae..62000529c5 100644 --- a/src/core/hle/service/olsc/olsc.h +++ b/src/core/hle/service/olsc/olsc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/olsc/olsc_service_for_application.cpp b/src/core/hle/service/olsc/olsc_service_for_application.cpp index 01360f5ef2..73d7d4ed64 100644 --- a/src/core/hle/service/olsc/olsc_service_for_application.cpp +++ b/src/core/hle/service/olsc/olsc_service_for_application.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/omm.cpp b/src/core/hle/service/omm/omm.cpp index b95319e26b..cc0b686ab1 100644 --- a/src/core/hle/service/omm/omm.cpp +++ b/src/core/hle/service/omm/omm.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/omm.h b/src/core/hle/service/omm/omm.h index 7bf04688ae..6b100a6d61 100644 --- a/src/core/hle/service/omm/omm.h +++ b/src/core/hle/service/omm/omm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/operation_mode_manager.cpp b/src/core/hle/service/omm/operation_mode_manager.cpp index fe7ed84a7f..1af7714d0d 100644 --- a/src/core/hle/service/omm/operation_mode_manager.cpp +++ b/src/core/hle/service/omm/operation_mode_manager.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/operation_mode_manager.h b/src/core/hle/service/omm/operation_mode_manager.h index 32bc7b2f93..dd6753757c 100644 --- a/src/core/hle/service/omm/operation_mode_manager.h +++ b/src/core/hle/service/omm/operation_mode_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/policy_manager_system.cpp b/src/core/hle/service/omm/policy_manager_system.cpp index 1cd6fd807b..b9e8277f8c 100644 --- a/src/core/hle/service/omm/policy_manager_system.cpp +++ b/src/core/hle/service/omm/policy_manager_system.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/policy_manager_system.h b/src/core/hle/service/omm/policy_manager_system.h index 151ca0d2ee..2fb6eb9293 100644 --- a/src/core/hle/service/omm/policy_manager_system.h +++ b/src/core/hle/service/omm/policy_manager_system.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/power_state_interface.cpp b/src/core/hle/service/omm/power_state_interface.cpp index 22cac82595..04ea75b496 100644 --- a/src/core/hle/service/omm/power_state_interface.cpp +++ b/src/core/hle/service/omm/power_state_interface.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/omm/power_state_interface.h b/src/core/hle/service/omm/power_state_interface.h index 825a6512d9..dc1d376a13 100644 --- a/src/core/hle/service/omm/power_state_interface.h +++ b/src/core/hle/service/omm/power_state_interface.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/multi_wait.cpp b/src/core/hle/service/os/multi_wait.cpp index 7b80d28bed..c9bb256ab0 100644 --- a/src/core/hle/service/os/multi_wait.cpp +++ b/src/core/hle/service/os/multi_wait.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/multi_wait.h b/src/core/hle/service/os/multi_wait.h index 340c611b5b..c9076c21d7 100644 --- a/src/core/hle/service/os/multi_wait.h +++ b/src/core/hle/service/os/multi_wait.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/multi_wait_holder.cpp b/src/core/hle/service/os/multi_wait_holder.cpp index 01efa045b5..f3864218c9 100644 --- a/src/core/hle/service/os/multi_wait_holder.cpp +++ b/src/core/hle/service/os/multi_wait_holder.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/multi_wait_holder.h b/src/core/hle/service/os/multi_wait_holder.h index 646395a3f3..3b28ec6b5a 100644 --- a/src/core/hle/service/os/multi_wait_holder.h +++ b/src/core/hle/service/os/multi_wait_holder.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/multi_wait_utils.h b/src/core/hle/service/os/multi_wait_utils.h index 96d3a10f37..d8f3ade4c1 100644 --- a/src/core/hle/service/os/multi_wait_utils.h +++ b/src/core/hle/service/os/multi_wait_utils.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/os/mutex.h b/src/core/hle/service/os/mutex.h index 95ac9b117a..8e29bbb633 100644 --- a/src/core/hle/service/os/mutex.h +++ b/src/core/hle/service/os/mutex.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h index 5c2d4b805b..cf2048ce65 100644 --- a/src/core/hle/service/pcie/pcie.h +++ b/src/core/hle/service/pcie/pcie.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pctl/parental_control_service_factory.h b/src/core/hle/service/pctl/parental_control_service_factory.h index 362988add1..0b5afede82 100644 --- a/src/core/hle/service/pctl/parental_control_service_factory.h +++ b/src/core/hle/service/pctl/parental_control_service_factory.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index d92dbe2169..37f18a5d58 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 5f9d03d4df..020866aa3d 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pctl/pctl_results.h b/src/core/hle/service/pctl/pctl_results.h index 1fc54727b7..0aa4714a9c 100644 --- a/src/core/hle/service/pctl/pctl_results.h +++ b/src/core/hle/service/pctl/pctl_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h index bf541e6feb..27eb179ff4 100644 --- a/src/core/hle/service/pcv/pcv.h +++ b/src/core/hle/service/pcv/pcv.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h index 5d4a1a171c..da70cfc6b8 100644 --- a/src/core/hle/service/pm/pm.h +++ b/src/core/hle/service/pm/pm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index 2c2462f936..4caf752888 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/ovln/ovln_types.h b/src/core/hle/service/psc/ovln/ovln_types.h index f96b52fb49..d827f039fe 100644 --- a/src/core/hle/service/psc/ovln/ovln_types.h +++ b/src/core/hle/service/psc/ovln/ovln_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/psc/ovln/receiver.h b/src/core/hle/service/psc/ovln/receiver.h index 2b96216a1a..5f73d1a66c 100644 --- a/src/core/hle/service/psc/ovln/receiver.h +++ b/src/core/hle/service/psc/ovln/receiver.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/psc/ovln/receiver_service.cpp b/src/core/hle/service/psc/ovln/receiver_service.cpp index d869325181..3b0b180725 100644 --- a/src/core/hle/service/psc/ovln/receiver_service.cpp +++ b/src/core/hle/service/psc/ovln/receiver_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default; Result IReceiverService::OpenReceiver(Out> out_receiver) { LOG_DEBUG(Service_PSC, "called"); - *out_receiver = std::shared_ptr(new IReceiver(system)); + *out_receiver = std::make_shared(system); R_SUCCEED(); } diff --git a/src/core/hle/service/psc/ovln/receiver_service.h b/src/core/hle/service/psc/ovln/receiver_service.h index b3b31ba4aa..a7a9b012e8 100644 --- a/src/core/hle/service/psc/ovln/receiver_service.h +++ b/src/core/hle/service/psc/ovln/receiver_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/ovln/sender.cpp b/src/core/hle/service/psc/ovln/sender.cpp index 3227a56f25..dffcd4bc36 100644 --- a/src/core/hle/service/psc/ovln/sender.cpp +++ b/src/core/hle/service/psc/ovln/sender.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/ovln/sender.h b/src/core/hle/service/psc/ovln/sender.h index c1575428e3..9b0fe786bd 100644 --- a/src/core/hle/service/psc/ovln/sender.h +++ b/src/core/hle/service/psc/ovln/sender.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/ovln/sender_service.cpp b/src/core/hle/service/psc/ovln/sender_service.cpp index 18d2c83a3d..8f80a5c461 100644 --- a/src/core/hle/service/psc/ovln/sender_service.cpp +++ b/src/core/hle/service/psc/ovln/sender_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/ovln/sender_service.h b/src/core/hle/service/psc/ovln/sender_service.h index 10027701f4..8176e85e23 100644 --- a/src/core/hle/service/psc/ovln/sender_service.h +++ b/src/core/hle/service/psc/ovln/sender_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_control.cpp b/src/core/hle/service/psc/pm_control.cpp index 7dedb76629..fc5852f863 100644 --- a/src/core/hle/service/psc/pm_control.cpp +++ b/src/core/hle/service/psc/pm_control.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_control.h b/src/core/hle/service/psc/pm_control.h index e0ae2f39ca..7967f7ab18 100644 --- a/src/core/hle/service/psc/pm_control.h +++ b/src/core/hle/service/psc/pm_control.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_module.cpp b/src/core/hle/service/psc/pm_module.cpp index 74dc7ed4e8..38ca36ab8f 100644 --- a/src/core/hle/service/psc/pm_module.cpp +++ b/src/core/hle/service/psc/pm_module.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_module.h b/src/core/hle/service/psc/pm_module.h index b3a2d25842..dac8fcff2a 100644 --- a/src/core/hle/service/psc/pm_module.h +++ b/src/core/hle/service/psc/pm_module.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_service.cpp b/src/core/hle/service/psc/pm_service.cpp index c4e0ad2286..d1803bdf48 100644 --- a/src/core/hle/service/psc/pm_service.cpp +++ b/src/core/hle/service/psc/pm_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/pm_service.h b/src/core/hle/service/psc/pm_service.h index 08e14c6f8f..721a0602c9 100644 --- a/src/core/hle/service/psc/pm_service.h +++ b/src/core/hle/service/psc/pm_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h index c83d07ca80..d89e6854cc 100644 --- a/src/core/hle/service/psc/psc.h +++ b/src/core/hle/service/psc/psc.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h index 0a68867d94..0abe200206 100644 --- a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp index 6a74d45946..d0e596c504 100644 --- a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h index 5722d8e965..832c6907a0 100644 --- a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp index 6938d369fe..b3143ead71 100644 --- a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h index bfafc7d71f..47a411e811 100644 --- a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp index 7a72d7aa25..8fc44fd691 100644 --- a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h index bbf98fcd5c..342d6385fd 100644 --- a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h index 32b8bc3bcd..14c8e613f4 100644 --- a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h index f933cc15f8..31be5e25ad 100644 --- a/src/core/hle/service/psc/time/clocks/steady_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp index 2b7466831d..7ca5e8515e 100644 --- a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h index 0b928432fb..d95bab5191 100644 --- a/src/core/hle/service/psc/time/clocks/system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp index 22da1fbccc..8666df57f1 100644 --- a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h index a7bea86a25..62890cd5ca 100644 --- a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp index a6d9f02ca3..dce2249ea1 100644 --- a/src/core/hle/service/psc/time/common.cpp +++ b/src/core/hle/service/psc/time/common.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h index 62ded12475..7c771d25f5 100644 --- a/src/core/hle/service/psc/time/manager.h +++ b/src/core/hle/service/psc/time/manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h index 30a0c947d6..325f73913b 100644 --- a/src/core/hle/service/psc/time/power_state_request_manager.h +++ b/src/core/hle/service/psc/time/power_state_request_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp index adef6bcd8b..b6841a87cb 100644 --- a/src/core/hle/service/psc/time/shared_memory.cpp +++ b/src/core/hle/service/psc/time/shared_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp index 81bfb90929..e537a821db 100644 --- a/src/core/hle/service/psc/time/time_zone.cpp +++ b/src/core/hle/service/psc/time/time_zone.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h index 0e4ed6ed00..68a235e440 100644 --- a/src/core/hle/service/psc/time/time_zone.h +++ b/src/core/hle/service/psc/time/time_zone.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index a0ae03d282..a62877e441 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h index a10a91a64b..d8a0d49bf8 100644 --- a/src/core/hle/service/ptm/ts.h +++ b/src/core/hle/service/ptm/ts.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 13de9f96c8..335efa3ad3 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -7,6 +7,7 @@ #include #include +#include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/cmif_serialization.h" @@ -16,6 +17,10 @@ #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" +#ifdef HAS_NCE +#include "core/arm/nce/patcher.h" +#include "core/hle/kernel/k_shared_memory.h" +#endif namespace Service::RO { @@ -386,7 +391,7 @@ public: R_SUCCEED(); } - Result MapManualLoadModuleMemory(u64* out_address, size_t context_id, u64 nro_address, + Result MapManualLoadModuleMemory(Kernel::KernelCore& kernel, u64* out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { // Get context. ProcessContext* context = this->GetContextById(context_id); @@ -421,10 +426,51 @@ public: R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), std::addressof(ro_size), std::addressof(rw_size), nro_info->base_address, nro_size, bss_size)); - - // Set NRO perms. - R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size, - rw_size + bss_size)); + R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size, rw_size + bss_size)); +#ifdef HAS_NCE + if (Settings::IsNceEnabled()) { + auto* process = context->GetProcess(); + auto& memory = process->GetMemory(); + std::vector text(rx_size); + memory.ReadBlock(nro_info->base_address, text.data(), text.size()); + Core::NCE::Patcher patch{text}; + const size_t patch_size = patch.GetSectionSize(); + if (patch_size != 0) { + constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute; + auto* patch_memory = Kernel::KSharedMemory::Create(kernel); + SCOPE_EXIT { patch_memory->Close(kernel); }; + R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size)); + Kernel::KSharedMemory::Register(kernel, patch_memory); + const std::array offsets{static_cast(total_size + Kernel::PageSize),-static_cast(patch_size + Kernel::PageSize)}; + std::vector tls_patches; + std::span instructions; + u64 patch_address = 0; + for (const s64 offset : offsets) { + instructions = patch.RelocateRuntimeTls(offset, tls_patches); + if (instructions.empty()) {continue;} + const u64 address = static_cast(static_cast(nro_info->base_address) + offset); + if (R_FAILED(patch_memory->Map(*process, address, patch_size, permission))) {continue;} + patch_address = address; + break; + } + R_UNLESS(patch_address != 0, RO::ResultOutOfAddressSpace); + const Result add_shared_memory_result = process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size); + if (R_FAILED(add_shared_memory_result)) { + R_ASSERT(patch_memory->Unmap(*process, patch_address, patch_size)); + } + R_TRY(add_shared_memory_result); + std::memcpy(patch_memory->GetPointer(), instructions.data(), instructions.size_bytes()); + auto* ptr = reinterpret_cast(patch_address); + __builtin___clear_cache(ptr, ptr + instructions.size_bytes()); + for (const auto& instruction : tls_patches) { + const u64 address = nro_info->base_address + instruction.offset; + memory.Write32(address, instruction.replacement); + ptr = reinterpret_cast(address); + __builtin___clear_cache(ptr, ptr + sizeof(u32)); + } + } + } +#endif context->SetNroInfoInUse(nro_info, true); nro_info->code_size = rx_size + ro_size; @@ -531,7 +577,7 @@ public: Result MapManualLoadModuleMemory(Out out_load_address, ClientProcessId client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, + R_RETURN(m_ro->MapManualLoadModuleMemory(system.Kernel(), out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); } diff --git a/src/core/hle/service/ro/ro.h b/src/core/hle/service/ro/ro.h index 74dc085368..c93aac3833 100644 --- a/src/core/hle/service/ro/ro.h +++ b/src/core/hle/service/ro/ro.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ro/ro_nro_utils.cpp b/src/core/hle/service/ro/ro_nro_utils.cpp index 268c7f93e7..1c2a97619d 100644 --- a/src/core/hle/service/ro/ro_nro_utils.cpp +++ b/src/core/hle/service/ro/ro_nro_utils.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ro/ro_nro_utils.h b/src/core/hle/service/ro/ro_nro_utils.h index f7083a1ba3..32b97abed2 100644 --- a/src/core/hle/service/ro/ro_nro_utils.h +++ b/src/core/hle/service/ro/ro_nro_utils.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h index 5173ce46e1..d86d20b7c5 100644 --- a/src/core/hle/service/server_manager.h +++ b/src/core/hle/service/server_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 225b92a783..b4fe2b3ed5 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -4,16 +4,12 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include -#include -#include #include "common/assert.h" #include "common/logging.h" #include "common/settings.h" #include "core/core.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/service.h" @@ -37,7 +33,6 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se : SessionRequestHandler(system_.Kernel(), service_name_) , system{system_} , service_name{service_name_} - , is_i_storage{std::string_view{service_name_} == "IStorage"} , handler_invoker{handler_invoker_} , max_sessions{max_sessions_} {} @@ -82,22 +77,13 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, } void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) { - const auto command = ctx.GetCommand(); - auto it = handlers.find(command); - const bool is_cmd_read = command == 0; + auto it = handlers.find(ctx.GetCommand()); FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second; if (info == nullptr || info->handler_callback == nullptr) return ReportUnimplementedFunction(ctx, info); LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); handler_invoker(this, info->handler_callback, ctx); - - if (is_i_storage && is_cmd_read) { - const auto* const process = ctx.GetThread().GetOwnerProcess(); - if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) { - std::this_thread::sleep_for(std::chrono::microseconds{600}); - } - } } void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) { diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index ea98897e79..68b0d636d8 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -107,8 +107,6 @@ protected: Core::System& system; /// Identifier string used to connect to the service. const char* service_name; - /// Whether this is the IStorage service. - const bool is_i_storage; /// Function used to safely up-cast pointers to the derived class before invoking a handler. InvokerFn* handler_invoker; /// Maximum number of concurrent sessions that this service can handle. diff --git a/src/core/hle/service/services.h b/src/core/hle/service/services.h index da5281ff29..4072afa3d1 100644 --- a/src/core/hle/service/services.h +++ b/src/core/hle/service/services.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/set/factory_settings_server.cpp b/src/core/hle/service/set/factory_settings_server.cpp index a8e307ae26..d663283c98 100644 --- a/src/core/hle/service/set/factory_settings_server.cpp +++ b/src/core/hle/service/set/factory_settings_server.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/factory_settings_server.h b/src/core/hle/service/set/factory_settings_server.h index e64cd13809..ddffcee643 100644 --- a/src/core/hle/service/set/factory_settings_server.h +++ b/src/core/hle/service/set/factory_settings_server.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/firmware_debug_settings_server.cpp b/src/core/hle/service/set/firmware_debug_settings_server.cpp index b7c66047e6..c491675567 100644 --- a/src/core/hle/service/set/firmware_debug_settings_server.cpp +++ b/src/core/hle/service/set/firmware_debug_settings_server.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/set/firmware_debug_settings_server.h b/src/core/hle/service/set/firmware_debug_settings_server.h index 5dae2263e9..05019faa9f 100644 --- a/src/core/hle/service/set/firmware_debug_settings_server.h +++ b/src/core/hle/service/set/firmware_debug_settings_server.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/key_code_map.h b/src/core/hle/service/set/key_code_map.h index c76dc57298..8e010e718b 100644 --- a/src/core/hle/service/set/key_code_map.h +++ b/src/core/hle/service/set/key_code_map.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/core/hle/service/set/setting_formats/appln_settings.cpp b/src/core/hle/service/set/setting_formats/appln_settings.cpp index f7c7d5b912..5726a41cdf 100644 --- a/src/core/hle/service/set/setting_formats/appln_settings.cpp +++ b/src/core/hle/service/set/setting_formats/appln_settings.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/appln_settings.h b/src/core/hle/service/set/setting_formats/appln_settings.h index ba9af998ab..2128a631e0 100644 --- a/src/core/hle/service/set/setting_formats/appln_settings.h +++ b/src/core/hle/service/set/setting_formats/appln_settings.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/device_settings.cpp b/src/core/hle/service/set/setting_formats/device_settings.cpp index 5f295404d9..6e9c321d8e 100644 --- a/src/core/hle/service/set/setting_formats/device_settings.cpp +++ b/src/core/hle/service/set/setting_formats/device_settings.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/private_settings.cpp b/src/core/hle/service/set/setting_formats/private_settings.cpp index 81c3624826..c3a67b60eb 100644 --- a/src/core/hle/service/set/setting_formats/private_settings.cpp +++ b/src/core/hle/service/set/setting_formats/private_settings.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h index 6579e95e04..75e3e03886 100644 --- a/src/core/hle/service/set/setting_formats/private_settings.h +++ b/src/core/hle/service/set/setting_formats/private_settings.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp index 258e213e13..fd70e2ace3 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.cpp +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h index 03cd4bb661..e6288af2e5 100644 --- a/src/core/hle/service/set/settings.h +++ b/src/core/hle/service/set/settings.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h index a9cee5f626..c37a65cbc9 100644 --- a/src/core/hle/service/set/settings_server.h +++ b/src/core/hle/service/set/settings_server.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -46,7 +49,7 @@ private: Result GetDeviceNickName( OutLargeData, BufferAttr_HipcMapAlias> out_device_name); - + Result GetKeyCodeMapByPort(OutLargeData out_key_code_map, u32 port); }; diff --git a/src/core/hle/service/sm/sm_controller.h b/src/core/hle/service/sm/sm_controller.h index f7e529a269..f1fa327ff7 100644 --- a/src/core/hle/service/sm/sm_controller.h +++ b/src/core/hle/service/sm/sm_controller.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index b542e196f0..5cc3eb5b81 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -1,5 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index bd6721fd3e..b1dfc71961 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h index dd7ba11f34..8c09be49ab 100644 --- a/src/core/hle/service/spl/spl_results.h +++ b/src/core/hle/service/spl/spl_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/spl/spl_types.h b/src/core/hle/service/spl/spl_types.h index 91f9cc0321..d7f0c4afbb 100644 --- a/src/core/hle/service/spl/spl_types.h +++ b/src/core/hle/service/spl/spl_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ssl/cert_store.cpp b/src/core/hle/service/ssl/cert_store.cpp index b321e5d32b..ee5bf8306b 100644 --- a/src/core/hle/service/ssl/cert_store.cpp +++ b/src/core/hle/service/ssl/cert_store.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ssl/cert_store.h b/src/core/hle/service/ssl/cert_store.h index 613d7b02af..4bbb3c9c1a 100644 --- a/src/core/hle/service/ssl/cert_store.h +++ b/src/core/hle/service/ssl/cert_store.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 750a7de236..999efd4295 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -48,6 +48,12 @@ enum class OptionType : u32 { EnableAlpn = 3, }; +// This is nn::ssl::sf::RenegotiationMode +enum RenegotiationMode : u32 { + None = 0, ///< None + Secure = 1, ///< Secure +}; + // This is nn::ssl::sf::SslVersion struct SslVersion { union { @@ -75,34 +81,34 @@ public: shared_data{shared_data_in}, backend{std::move(backend_in)} { // clang-format off static const FunctionInfo functions[] = { - {0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"}, - {1, &ISslConnection::SetHostName, "SetHostName"}, - {2, &ISslConnection::SetVerifyOption, "SetVerifyOption"}, - {3, &ISslConnection::SetIoMode, "SetIoMode"}, - {4, nullptr, "GetSocketDescriptor"}, - {5, nullptr, "GetHostName"}, + {0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"}, + {1, D<&ISslConnection::SetHostName>, "SetHostName"}, + {2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"}, + {3, D<&ISslConnection::SetIoMode>, "SetIoMode"}, + {4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"}, + {5, D<&ISslConnection::GetHostName>, "GetHostName"}, {6, nullptr, "GetVerifyOption"}, - {7, nullptr, "GetIoMode"}, - {8, &ISslConnection::DoHandshake, "DoHandshake"}, + {7, D<&ISslConnection::GetIoMode>, "GetIoMode"}, + {8, D<&ISslConnection::DoHandshake>, "DoHandshake"}, {9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"}, - {10, &ISslConnection::Read, "Read"}, - {11, &ISslConnection::Write, "Write"}, - {12, &ISslConnection::Pending, "Pending"}, - {13, nullptr, "Peek"}, - {14, nullptr, "Poll"}, - {15, nullptr, "GetVerifyCertError"}, - {16, nullptr, "GetNeededServerCertBufferSize"}, - {17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"}, - {18, nullptr, "GetSessionCacheMode"}, - {19, nullptr, "FlushSessionCache"}, - {20, nullptr, "SetRenegotiationMode"}, - {21, nullptr, "GetRenegotiationMode"}, - {22, &ISslConnection::SetOption, "SetOption"}, - {23, &ISslConnection::GetOption, "GetOption"}, + {10, D<&ISslConnection::Read>, "Read"}, + {11, D<&ISslConnection::Write>, "Write"}, + {12, D<&ISslConnection::Pending>, "Pending"}, + {13, D<&ISslConnection::Peek>, "Peek"}, + {14, D<&ISslConnection::Poll>, "Poll"}, + {15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"}, + {16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"}, + {17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"}, + {18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"}, + {19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"}, + {20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"}, + {21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"}, + {22, D<&ISslConnection::SetOption>, "SetOption"}, + {23, D<&ISslConnection::GetOption>, "GetOption"}, {24, nullptr, "GetVerifyCertErrors"}, {25, nullptr, "GetCipherInfo"}, - {26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"}, - {27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"}, + {26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"}, + {27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"}, {28, nullptr, "SetDtlsSocketDescriptor"}, {29, nullptr, "GetDtlsHandshakeTimeout"}, {30, nullptr, "SetPrivateOption"}, @@ -141,80 +147,6 @@ public: } private: - SslVersion ssl_version; - std::shared_ptr shared_data; - std::unique_ptr backend; - std::optional fd_to_close; - bool do_not_close_socket = false; - bool get_server_cert_chain = false; - bool skip_default_verify = false; - bool enable_alpn = false; - std::shared_ptr socket; - std::vector next_alpn_proto; - bool did_handshake = false; - u32 verify_option = 0; - - Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) { - LOG_DEBUG(Service_SSL, "called, fd={}", fd); - ASSERT(!did_handshake); - auto bsd = system.ServiceManager().GetService("bsd:u"); - ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); - - auto const res_v = bsd->DuplicateSocketImpl(fd); - if (auto *res = std::get_if(&res_v)) { - const s32 duplicated_fd = *res; - if (do_not_close_socket) { - *out_fd = duplicated_fd; - } else { - *out_fd = -1; - fd_to_close = duplicated_fd; - } - std::optional> sock = bsd->GetSocket(duplicated_fd); - if (!sock.has_value()) { - LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd); - return ResultInvalidSocket; - } - socket = std::move(*sock); - backend->SetSocket(socket); - return ResultSuccess; - } - LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd); - return ResultInvalidSocket; - } - - Result SetHostNameImpl(const std::string& hostname) { - LOG_DEBUG(Service_SSL, "called. hostname={}", hostname); - ASSERT(!did_handshake); - return backend->SetHostName(hostname); - } - - Result SetVerifyOptionImpl(u32 option) { - ASSERT(!did_handshake); - LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option); - verify_option = 0; - backend->SetVerifyOption(0); - return ResultSuccess; - } - - Result SetIoModeImpl(u32 input_mode) { - auto mode = static_cast(input_mode); - ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking); - ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; }); - - const bool non_block = mode == IoMode::NonBlocking; - const Network::Errno error = socket->SetNonBlock(non_block); - if (error != Network::Errno::SUCCESS) { - LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block); - } - return ResultSuccess; - } - - Result SetSessionCacheModeImpl(u32 mode) { - ASSERT(!did_handshake); - LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode); - return ResultSuccess; - } - Result DoHandshakeImpl() { ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; }); Result res = backend->DoHandshake(); @@ -234,19 +166,17 @@ private: }; if (!get_server_cert_chain) { // Just return the first one, unencoded. - ASSERT_OR_EXECUTE_MSG( - !certs.empty(), { return {}; }, "Should be at least one server cert"); + ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert"); return certs[0]; } std::vector ret; - Header header{0x4E4D684374726543, static_cast(certs.size()), 0}; + Header header{0x4E4D684374726543, u32(certs.size()), 0}; ret.insert(ret.end(), reinterpret_cast(&header), reinterpret_cast(&header + 1)); size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader); for (auto& cert : certs) { - EntryHeader entry_header{static_cast(cert.size()), static_cast(data_offset)}; + EntryHeader entry_header{u32(cert.size()), u32(data_offset)}; data_offset += cert.size(); - ret.insert(ret.end(), reinterpret_cast(&entry_header), - reinterpret_cast(&entry_header + 1)); + ret.insert(ret.end(), reinterpret_cast(&entry_header), reinterpret_cast(&entry_header + 1)); } for (auto& cert : certs) { ret.insert(ret.end(), cert.begin(), cert.end()); @@ -254,65 +184,77 @@ private: return ret; } - Result ReadImpl(std::vector* out_data) { - ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); - size_t actual_size{}; - Result res = backend->Read(&actual_size, *out_data); - if (res != ResultSuccess) { - return res; + Result SetSocketDescriptor(s32 in_fd, Out out_fd) { + LOG_DEBUG(Service_SSL, "called, fd={}", in_fd); + ASSERT(!did_handshake); + auto bsd = system.ServiceManager().GetService("bsd:u"); + ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); + + auto const res_v = bsd->DuplicateSocketImpl(in_fd); + if (auto *res = std::get_if(&res_v)) { + const s32 dup_fd = *res; + *out_fd = do_not_close_socket ? dup_fd : -1; + if (!do_not_close_socket) + fd_to_close = dup_fd; + auto const sock = bsd->GetSocket(dup_fd); + if (!sock.has_value()) { + LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", dup_fd); + return ResultInvalidSocket; + } + socket = std::move(*sock); + backend->SetSocket(std::move(socket)); + return ResultSuccess; } - out_data->resize(actual_size); - return res; + LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd); + return ResultInvalidSocket; } - Result WriteImpl(size_t* out_size, std::span data) { - ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); - return backend->Write(out_size, data); + Result SetHostName(InBuffer buf) { + auto const hostname = Common::StringFromBuffer(buf); + LOG_DEBUG(Service_SSL, "called. hostname={}", hostname); + ASSERT(!did_handshake); + return backend->SetHostName(hostname.c_str()); } - Result PendingImpl(s32* out_pending) { - LOG_WARNING(Service_SSL, "(STUBBED) called."); - *out_pending = 0; - return ResultSuccess; + Result SetVerifyOption(u32 option) { + LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option); + ASSERT(!did_handshake); + verify_option = 0; + backend->SetVerifyOption(0); + R_SUCCEED(); } - void SetSocketDescriptor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const s32 in_fd = rp.Pop(); - s32 out_fd{-1}; - const Result res = SetSocketDescriptorImpl(&out_fd, in_fd); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.Push(out_fd); + Result SetIoMode(u32 input_mode) { + auto mode = IoMode(input_mode); + ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking); + R_UNLESS(socket, ResultNoSocket); + const bool non_block = mode == IoMode::NonBlocking; + const Network::Errno error = socket->SetNonBlock(non_block); + if (error != Network::Errno::SUCCESS) { + LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block); + } + R_SUCCEED(); } - void SetHostName(HLERequestContext& ctx) { - const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer()); - const Result res = SetHostNameImpl(hostname); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + Result GetSocketDescriptor(Out out_fd) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + *out_fd = uint32_t(socket->GetFD()); + R_SUCCEED(); } - void SetVerifyOption(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 option = rp.Pop(); - const Result res = SetVerifyOptionImpl(option); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + Result GetHostName(OutBuffer data, Out out_size) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + ASSERT(!did_handshake); + return backend->GetHostName(std::span{data.begin(), data.end()}, out_size); } - void SetIoMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 mode = rp.Pop(); - const Result res = SetIoModeImpl(mode); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + Result GetIoMode(Out out_mode) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_SUCCEED(); } - void DoHandshake(HLERequestContext& ctx) { - const Result res = DoHandshakeImpl(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + Result DoHandshake() { + return DoHandshakeImpl(); } void DoHandshakeGetServerCert(HLERequestContext& ctx) { @@ -351,131 +293,158 @@ private: rb.PushRaw(out); } - void Read(HLERequestContext& ctx) { - std::vector output_bytes(ctx.GetWriteBufferSize()); - const Result res = ReadImpl(&output_bytes); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - if (res == ResultSuccess) { - rb.Push(static_cast(output_bytes.size())); - ctx.WriteBuffer(output_bytes); - } else { - rb.Push(static_cast(0)); - } + Result Read(OutBuffer data, Out out_size) { + R_UNLESS(did_handshake, ResultInternalError); + size_t tmp{}; + auto const res = backend->Read(&tmp, data); + *out_size = u32(tmp); + return res; } - void Write(HLERequestContext& ctx) { - size_t write_size{0}; - const Result res = WriteImpl(&write_size, ctx.ReadBuffer()); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.Push(static_cast(write_size)); + Result Write(InBuffer data, Out out_size) { + R_UNLESS(did_handshake, ResultInternalError); + size_t tmp{}; + auto const res = backend->Write(&tmp, data); + *out_size = u32(tmp); + return res; } - void Pending(HLERequestContext& ctx) { - s32 pending_size{0}; - const Result res = PendingImpl(&pending_size); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.Push(pending_size); + Result Pending(Out out_pending_size) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + *out_pending_size = s32(backend->Pending()); + R_SUCCEED(); } - void SetSessionCacheMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 mode = rp.Pop(); - const Result res = SetSessionCacheModeImpl(mode); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + Result Peek(OutBuffer data, Out out_size) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + size_t tmp{}; + auto const res = backend->Peek(&tmp, data); + *out_size = u32(tmp); + return res; } - void SetOption(HLERequestContext& ctx) { - struct Parameters { - OptionType option; - s32 value; - }; - static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size"); - - IPC::RequestParser rp{ctx}; - const auto parameters = rp.PopRaw(); - - switch (parameters.option) { - case OptionType::DoNotCloseSocket: - do_not_close_socket = static_cast(parameters.value); - break; - case OptionType::GetServerCertChain: - get_server_cert_chain = static_cast(parameters.value); - break; - case OptionType::SkipDefaultVerify: - skip_default_verify = static_cast(parameters.value); - break; - case OptionType::EnableAlpn: - enable_alpn = static_cast(parameters.value); - break; - default: - LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option, - parameters.value); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + Result Poll(u32 in_pollevent, u32 timer, Out out_pollevent) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_SUCCEED(); } - void GetOption(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto option = rp.PopRaw(); + Result GetVerifyCertError() { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_SUCCEED(); + } - u8 value = 0; + Result GetNeededServerCertBufferSize(Out out_needed_buffer_size) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_SUCCEED(); + } + Result SetSessionCacheMode(u32 mode) { + LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode); + R_UNLESS(!did_handshake, ResultInternalError); + R_SUCCEED(); + } + + Result GetSessionCacheMode(Out mode) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_UNLESS(!did_handshake, ResultInternalError); + R_SUCCEED(); + } + + Result FlushSessionCache() { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_UNLESS(!did_handshake, ResultInternalError); + R_SUCCEED(); + } + + Result SetRenegotiationMode(RenegotiationMode mode) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + backend->SetRenegotiationMode(u32(mode)); + R_SUCCEED(); + } + + Result GetRenegotiationMode(Out mode) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + u32 tmp{}; + auto const res = backend->GetRenegotiationMode(&tmp); + *mode = RenegotiationMode(tmp); + return res; + } + + Result SetOption(OptionType option, s32 value) { switch (option) { case OptionType::DoNotCloseSocket: - value = static_cast(do_not_close_socket); + do_not_close_socket = bool(value); break; case OptionType::GetServerCertChain: - value = static_cast(get_server_cert_chain); + get_server_cert_chain = bool(value); break; case OptionType::SkipDefaultVerify: - value = static_cast(skip_default_verify); + skip_default_verify = bool(value); break; case OptionType::EnableAlpn: - value = static_cast(enable_alpn); + enable_alpn = bool(value); break; default: - LOG_WARNING(Service_SSL, "Unknown option={}", option); - value = 0; + LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value); + } + R_SUCCEED(); + } + + Result GetOption(OptionType option, Out value) { + switch (option) { + case OptionType::DoNotCloseSocket: + *value = u8(do_not_close_socket); + break; + case OptionType::GetServerCertChain: + *value = u8(get_server_cert_chain); + break; + case OptionType::SkipDefaultVerify: + *value = u8(skip_default_verify); + break; + case OptionType::EnableAlpn: + *value = u8(enable_alpn); + break; + default: + LOG_WARNING(Service_SSL, "Unknown option={}", u32(option)); + *value = 0; break; } - - LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(value); + LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value); + R_SUCCEED(); } - void SetNextAlpnProto(HLERequestContext& ctx) { - const auto data = ctx.ReadBuffer(0); - next_alpn_proto.assign(data.begin(), data.end()); - + Result SetNextAlpnProto(InBuffer data) { + auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size())); + next_alpn_proto.assign(data.begin(), data.begin() + to_write); LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetNextAlpnProto(HLERequestContext& ctx) { - const size_t writable = ctx.GetWriteBufferSize(); - const size_t to_write = (std::min)(next_alpn_proto.size(), writable); - - if (to_write != 0) { - ctx.WriteBuffer(std::span(next_alpn_proto.data(), to_write)); - } - - LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(to_write)); + Result GetNextAlpnProto(OutBuffer data, Out to_write) { + *to_write = u32((std::min)(next_alpn_proto.size(), data.size())); + next_alpn_proto.assign(data.begin(), data.begin() + *to_write); + LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write); + R_SUCCEED(); } + + Result GetVerifyCertErrors(OutBuffer unk0, Out unk1, Out unk2) { + LOG_WARNING(Service_SSL, "(STUBBED)"); + R_SUCCEED(); + } + + SslVersion ssl_version; + std::shared_ptr shared_data; + std::unique_ptr backend; + std::optional fd_to_close; + std::shared_ptr socket; + std::vector next_alpn_proto; + u32 verify_option = 0; + + bool do_not_close_socket = false; + bool get_server_cert_chain = false; + bool skip_default_verify = false; + bool enable_alpn = false; + bool did_handshake = false; }; class ISslContext final : public ServiceFramework { @@ -492,7 +461,7 @@ public: {5, &ISslContext::ImportClientPki, "ImportClientPki"}, {6, nullptr, "RemoveServerPki"}, {7, nullptr, "RemoveClientPki"}, - {8, nullptr, "RegisterInternalPki"}, + {8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"}, {9, nullptr, "AddPolicyOid"}, {10, nullptr, "ImportCrl"}, {11, nullptr, "RemoveCrl"}, @@ -587,6 +556,11 @@ private: rb.Push(ResultSuccess); rb.Push(client_id); } + + Result RegisterInternalPki() { + LOG_WARNING(Service_SSL, "(STUBBED) called"); + R_SUCCEED(); + } }; class ISslService final : public ServiceFramework { diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h index f6e21bbb3b..dc9f2b746b 100644 --- a/src/core/hle/service/ssl/ssl.h +++ b/src/core/hle/service/ssl/ssl.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/ssl/ssl_backend.h b/src/core/hle/service/ssl/ssl_backend.h index a3127342a2..ef6bf8f9de 100644 --- a/src/core/hle/service/ssl/ssl_backend.h +++ b/src/core/hle/service/ssl/ssl_backend.h @@ -36,12 +36,17 @@ class SSLConnectionBackend { public: virtual ~SSLConnectionBackend() {} virtual void SetSocket(std::shared_ptr socket) = 0; - virtual Result SetHostName(const std::string& hostname) = 0; virtual void SetVerifyOption(u32 option) = 0; virtual Result DoHandshake() = 0; virtual Result Read(size_t* out_size, std::span data) = 0; + virtual Result Peek(size_t* out_size, std::span data) = 0; virtual Result Write(size_t* out_size, std::span data) = 0; virtual Result GetServerCerts(std::vector>* out_certs) = 0; + virtual Result SetHostName(const char* hostname) = 0; + virtual Result GetHostName(std::span hostname, u32* out_size) = 0; + virtual int Pending() = 0; + virtual Result SetRenegotiationMode(u32 mode) = 0; + virtual Result GetRenegotiationMode(u32* mode) = 0; }; Result CreateSSLConnectionBackend(std::unique_ptr* out_backend); diff --git a/src/core/hle/service/ssl/ssl_backend_openssl.cpp b/src/core/hle/service/ssl/ssl_backend_openssl.cpp index f0e05266ba..435ada845d 100644 --- a/src/core/hle/service/ssl/ssl_backend_openssl.cpp +++ b/src/core/hle/service/ssl/ssl_backend_openssl.cpp @@ -157,18 +157,30 @@ public: socket = std::move(socket_in); } - Result SetHostName(const std::string& hostname) override { + Result SetHostName(const char* hostname) override { if (!skip_cert_verification) { - if (!SSL_set1_host(ssl, hostname.c_str())) { + if (!SSL_set1_host(ssl, hostname)) { LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname); return CheckOpenSSLErrors(); } } - if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI + if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname); return CheckOpenSSLErrors(); } - return ResultSuccess; + R_SUCCEED(); + } + + Result GetHostName(std::span data, u32* out_size) override { + auto const peer_name = SSL_get0_peername(ssl); + if (peer_name == nullptr) { + LOG_ERROR(Service_SSL, "SSL_get0_peername()"); + return CheckOpenSSLErrors(); + } + auto const s = std::string{peer_name}; + *out_size = u32(s.size()); + std::memcpy(data.data(), s.data(), (std::min)(s.size(), data.size())); + R_SUCCEED(); } void SetVerifyOption(u32 option) override { @@ -213,6 +225,13 @@ public: return HandleReturn("SSL_read_ex", out_size, ret); } + Result Peek(size_t* out_size, std::span data) override { + auto const n = (std::min)(data.size(), *out_size); + const int ret = SSL_peek(ssl, data.data(), int(n)); + *out_size = n; + return HandleReturn("SSL_write_ex", out_size, ret); + } + Result Write(size_t* out_size, std::span data) override { const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size); return HandleReturn("SSL_write_ex", out_size, ret); @@ -263,7 +282,25 @@ public: out_certs->emplace_back(buf, buf + len); OPENSSL_free(buf); } - return ResultSuccess; + R_SUCCEED(); + } + + int Pending() override { + return SSL_pending(ssl); + } + + Result SetRenegotiationMode(u32 mode) override { + if (mode == 0) { + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_RENEGOTIATION); + } else { + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALLOW_CLIENT_RENEGOTIATION); + } + R_SUCCEED(); + } + + Result GetRenegotiationMode(u32* mode) override { + *mode = SSL_get_secure_renegotiation_support(ssl) ? 1 : 0; + R_SUCCEED(); } ~SSLConnectionBackendOpenSSL() { diff --git a/src/core/hle/service/ssl/ssl_types.h b/src/core/hle/service/ssl/ssl_types.h index 16623c0fc5..cbd84ad1f9 100644 --- a/src/core/hle/service/ssl/ssl_types.h +++ b/src/core/hle/service/ssl/ssl_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h index 98376ebc0c..cc25a3b29e 100644 --- a/src/core/hle/service/usb/usb.h +++ b/src/core/hle/service/usb/usb.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/application_root_service.cpp b/src/core/hle/service/vi/application_root_service.cpp index 7f35a048da..2204f77ed4 100644 --- a/src/core/hle/service/vi/application_root_service.cpp +++ b/src/core/hle/service/vi/application_root_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/application_root_service.h b/src/core/hle/service/vi/application_root_service.h index 15aa4483d6..b66293b73f 100644 --- a/src/core/hle/service/vi/application_root_service.h +++ b/src/core/hle/service/vi/application_root_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/display.h b/src/core/hle/service/vi/display.h index fceda75e39..543c6ee68d 100644 --- a/src/core/hle/service/vi/display.h +++ b/src/core/hle/service/vi/display.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/display_list.h b/src/core/hle/service/vi/display_list.h index f710ac472c..bf9cf78a27 100644 --- a/src/core/hle/service/vi/display_list.h +++ b/src/core/hle/service/vi/display_list.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/layer.h b/src/core/hle/service/vi/layer.h index e4c9c98646..9bd6d033ac 100644 --- a/src/core/hle/service/vi/layer.h +++ b/src/core/hle/service/vi/layer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/layer_list.h b/src/core/hle/service/vi/layer_list.h index 4afca6f403..8fc7cd3ade 100644 --- a/src/core/hle/service/vi/layer_list.h +++ b/src/core/hle/service/vi/layer_list.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/manager_display_service.cpp b/src/core/hle/service/vi/manager_display_service.cpp index e4d22ca11c..34a0175b49 100644 --- a/src/core/hle/service/vi/manager_display_service.cpp +++ b/src/core/hle/service/vi/manager_display_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/vi/manager_display_service.h b/src/core/hle/service/vi/manager_display_service.h index 14a107cb43..2e05e730c7 100644 --- a/src/core/hle/service/vi/manager_display_service.h +++ b/src/core/hle/service/vi/manager_display_service.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/vi/manager_root_service.cpp b/src/core/hle/service/vi/manager_root_service.cpp index 0f16a15b4a..094261e060 100644 --- a/src/core/hle/service/vi/manager_root_service.cpp +++ b/src/core/hle/service/vi/manager_root_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/manager_root_service.h b/src/core/hle/service/vi/manager_root_service.h index 77cd328699..114dc36c07 100644 --- a/src/core/hle/service/vi/manager_root_service.h +++ b/src/core/hle/service/vi/manager_root_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/service_creator.cpp b/src/core/hle/service/vi/service_creator.cpp index 2b8e5f9577..34dbf7baae 100644 --- a/src/core/hle/service/vi/service_creator.cpp +++ b/src/core/hle/service/vi/service_creator.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/service_creator.h b/src/core/hle/service/vi/service_creator.h index c6ba1797dc..8b50e94af5 100644 --- a/src/core/hle/service/vi/service_creator.h +++ b/src/core/hle/service/vi/service_creator.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp index 0956053dcf..aa225f69df 100644 --- a/src/core/hle/service/vi/system_display_service.cpp +++ b/src/core/hle/service/vi/system_display_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/vi/system_root_service.cpp b/src/core/hle/service/vi/system_root_service.cpp index 3489727d81..c761f136e2 100644 --- a/src/core/hle/service/vi/system_root_service.cpp +++ b/src/core/hle/service/vi/system_root_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/system_root_service.h b/src/core/hle/service/vi/system_root_service.h index 9d5aa53d38..1587e0e30f 100644 --- a/src/core/hle/service/vi/system_root_service.h +++ b/src/core/hle/service/vi/system_root_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index b388efaf6f..2ea64f5d32 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 7c1f350d84..bcc55228aa 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h index 22bac799f0..89b7317792 100644 --- a/src/core/hle/service/vi/vi_results.h +++ b/src/core/hle/service/vi/vi_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/hle/service/vi/vi_types.h b/src/core/hle/service/vi/vi_types.h index 95ff663582..2d4287169a 100644 --- a/src/core/hle/service/vi/vi_types.h +++ b/src/core/hle/service/vi/vi_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/core/launch_params.cpp b/src/core/launch_params.cpp new file mode 100644 index 0000000000..a0483226ec --- /dev/null +++ b/src/core/launch_params.cpp @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "common/assert.h" +#include "common/logging.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "common/scm_rev.h" +#include "core/core.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/launch_params.h" + +#undef _UNICODE +#include +#ifndef _MSC_VER +#include +#endif + +namespace Core { +LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept { + LaunchParams p{}; + + int option_index = 0; + static struct option long_options[] = { + // clang-format off + {"debug", no_argument, 0, 'd'}, + {"config", required_argument, 0, 'c'}, + {"fullscreen", no_argument, 0, 'f'}, + {"help", no_argument, 0, 'h'}, + {"game", required_argument, 0, 'g'}, + {"multiplayer", required_argument, 0, 'm'}, + {"program", optional_argument, 0, 'p'}, + {"user", required_argument, 0, 'u'}, + {"version", no_argument, 0, 'v'}, + {"input-profile", no_argument, 0, 'i'}, + {"null-render", no_argument, 0, 'n'}, + {"singlecore", no_argument, 0, 's'}, + {"filter", no_argument, 0, 'x'}, + {0, 0, 0, 0}, + // clang-format on + }; + + while (optind < argc) { + if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) { + switch (char(arg)) { + case 'd': + p.override_gdb_port = uint16_t(atoi(optarg)); + break; + case 'c': + p.config_path = optarg; + break; + case 'f': + p.fullscreen = true; + LOG_INFO(Frontend, "Starting in fullscreen mode..."); + break; + case 'h': + p.print_help = true; + break; + case 'g': + p.filepath = std::string(optarg); + break; + case 'i': { + p.input_profile = std::string(optarg); + break; + } + case 'm': { + p.use_multiplayer = true; + const std::string str_arg(optarg); + // regex to check if the format is nickname:password@ip:port + // with optional :password + const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); + if (std::regex_match(str_arg, re)) { + std::smatch match; + std::regex_search(str_arg, match, re); + ASSERT(match.size() == 5); + p.nickname = match[1]; + p.password = match[2]; + p.address = match[3]; + if (!match[4].str().empty()) { + p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0)); + } + std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); + ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters"); + ASSERT(!p.address.empty() && "Address is empty"); + } + break; + } + case 'p': + p.program_args = argv[optind]; + ++optind; + break; + case 'u': { + // Launch game with a specific user + bool argument_ok = isdigit(optarg[0]); + p.selected_user = atoi(optarg); + if (!argument_ok) { + // try to look it up by username, only finds the first username that matches. + auto const user_idx = system.GetProfileManager().GetUserIndex(optarg); + if (user_idx != std::nullopt) { + p.selected_user = user_idx.value(); + } else { + LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg); + break; + } + } + if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) { + Settings::values.current_user = s32(*p.selected_user); + } else { + LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user); + } + break; + } + case 'v': + p.print_version = true; + break; + case 'n': + p.force_null_render = true; + break; + case 's': + p.force_single_core = true; + break; + case 'x': + p.log_filter = argv[optind]; + ++optind; + break; + } + } else { + // only kept due to shortcuts made by Qt frontend which use "-qlaunch" + if (strcmp(argv[optind], "-hlaunch") == 0) { + p.launch_hlaunch = true; + } else if (strcmp(argv[optind], "-qlaunch") == 0) { + p.launch_qlaunch = true; + } else if (strcmp(argv[optind], "-setup") == 0) { + p.launch_setup = true; + } else { + // qt feeds utf8 data, sdl frontend feeds raw utf16 data +#ifdef _WIN32 + p.filepath = argv_w != nullptr + ? Common::UTF16ToUTF8(argv_w[optind]) + : argv[optind]; +#else + p.filepath = argv[optind]; +#endif + } + optind++; + } + } + p.argv0 = argv[0]; + return p; +} + +void ApplyLaunchParams(LaunchParams const& lp) noexcept { + // apply the log_filter setting + // the logger was initialized before and doesn't pick up the filter on its own + Common::Log::Filter filter{}; + filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue())); + Common::Log::SetGlobalFilter(filter); + + if (!lp.program_args.empty()) { + Settings::values.program_args = lp.program_args; + } + if (!lp.input_profile.empty()) { + auto& players = Settings::values.players.GetValue(); + players[0].profile_name = lp.input_profile; + } + if (lp.selected_user.has_value()) { + Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7); + } + if (lp.override_gdb_port.has_value()) { + Settings::values.use_gdbstub = true; + Settings::values.gdbstub_port = *lp.override_gdb_port; + } + if (lp.force_single_core) { + Settings::values.use_multi_core = false; + } + if (lp.force_null_render) { + Settings::values.renderer_backend = Settings::RendererBackend::Null; + } + + if (lp.print_version) { + LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc); + } + if (lp.print_help) { + LOG_INFO(Frontend, + "Usage: {}" + " [options] \n" + "-c, --config Load the specified configuration file\n" + "-f, --fullscreen Start in fullscreen mode\n" + "-g, --game File path of the game to load\n" + "-h, --help Display this help and exit\n" + "-m, --multiplayer=nick:password@address:port" + " Nickname, password, address and port for multiplayer\n" + "-p, --program Pass following string as arguments to executable\n" + "-u, --user Select a specific user profile from 0 to 7\n" + "-d, --debug Run the GDB stub on a port from 1 to 65535\n" + "-v, --version Output version information and exit\n", + lp.argv0 + ); + } +} + +} diff --git a/src/core/launch_params.h b/src/core/launch_params.h new file mode 100644 index 0000000000..4da30fc1c2 --- /dev/null +++ b/src/core/launch_params.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "network/room.h" + +namespace Core { + +class System; +struct LaunchParams { + std::string argv0{}; + std::string filepath{}; + std::string nickname{}; + std::string password{}; + std::string address{}; + std::string input_profile{}; + std::string program_args{}; + std::optional config_path{}; + std::optional selected_user{}; + std::optional override_gdb_port{}; + std::optional log_filter{}; + u16 port = Network::DefaultRoomPort; + bool use_multiplayer = false; + bool fullscreen = false; + bool force_null_render = false; + bool force_single_core = false; + // print + bool print_version = false; + bool print_help = false; + // qt + bool launch_hlaunch = false; + bool launch_qlaunch = false; + bool launch_setup = false; +}; + +LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept; +void ApplyLaunchParams(LaunchParams const& lp) noexcept; + +} diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index b04da7e84f..5e2ec7e0b9 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -182,12 +182,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } metadata.Print(); - if (Settings::values.relocate_36bit_address_space.GetValue() && - Settings::values.cpu_backend.GetValue() == Settings::CpuBackend::Nce && - metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) { - metadata.SetAddressSpaceType(FileSys::ProgramAddressSpaceType::Is39Bit); - } - // Enable NCE only for applications with 39-bit address space. const bool is_39bit = metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 90e23ccc11..92e2e2f602 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -82,23 +82,27 @@ std::shared_ptr OpenContainerAsNsp(FileSys::VirtualFile file, File return nullptr; } - if (type == FileType::NSP) { + if (type == FileType::NSP || type == FileType::NSZ) { auto nsp = std::make_shared(file, program_id, program_index); return nsp->GetStatus() == ResultStatus::Success ? nsp : nullptr; } - if (type == FileType::XCI) { + if (type == FileType::XCI || type == FileType::XCZ) { FileSys::XCI xci{file, program_id, program_index}; - if (xci.GetStatus() != ResultStatus::Success) { - return nullptr; + if (xci.GetStatus() == ResultStatus::Success) { + auto secure_nsp = xci.GetSecurePartitionNSP(); + if (secure_nsp != nullptr && secure_nsp->GetStatus() == ResultStatus::Success) { + return secure_nsp; + } } - auto secure_nsp = xci.GetSecurePartitionNSP(); - if (secure_nsp == nullptr || secure_nsp->GetStatus() != ResultStatus::Success) { - return nullptr; + // PFS0-shaped .xcz (NSZ-style container) does not parse as XCI; fall back to NSP. + if (type == FileType::XCZ) { + auto nsp = std::make_shared(file, program_id, program_index); + return nsp->GetStatus() == ResultStatus::Success ? nsp : nullptr; } - return secure_nsp; + return nullptr; } return nullptr; @@ -144,7 +148,8 @@ FileType IdentifyFile(FileSys::VirtualFile file) { } bool IsContainerType(FileType type) { - return type == FileType::NSP || type == FileType::XCI; + return type == FileType::NSP || type == FileType::NSZ || type == FileType::XCI || + type == FileType::XCZ; } bool IsBootableGameContainer(FileSys::VirtualFile file, FileType type, u64 program_id, @@ -180,8 +185,12 @@ FileType GuessFromFilename(const std::string& name) { return FileType::NCA; else if (extension == "xci") return FileType::XCI; + else if (extension == "xcz") + return FileType::XCZ; else if (extension == "nsp") return FileType::NSP; + else if (extension == "nsz") + return FileType::NSZ; else if (extension == "kip") return FileType::KIP; return FileType::Unknown; @@ -197,10 +206,14 @@ std::string GetFileTypeString(FileType type) { return "NCA"; case FileType::XCI: return "XCI"; + case FileType::XCZ: + return "XCZ"; case FileType::NAX: return "NAX"; case FileType::NSP: return "NSP"; + case FileType::NSZ: + return "NSZ"; case FileType::KIP: return "KIP"; case FileType::DeconstructedRomDirectory: @@ -326,12 +339,29 @@ static std::unique_ptr GetFileLoader(Core::System& system, FileSys::V system.GetContentProvider(), program_id, program_index); + // NCZ-compressed XCI container. XCI-shaped .xcz goes to the XCI loader, while + // PFS0-shaped (NSZ-style) .xcz containers fall back to the NSP loader. + case FileType::XCZ: { + FileSys::XCI probe{file, program_id, program_index}; + if (probe.GetStatus() == ResultStatus::Success && + probe.GetSecurePartitionNSP() != nullptr) { + return std::make_unique(std::move(file), + system.GetFileSystemController(), + system.GetContentProvider(), program_id, + program_index); + } + return std::make_unique(std::move(file), system.GetFileSystemController(), + system.GetContentProvider(), program_id, + program_index); + } + // NX NAX (NintendoAesXts) file format. case FileType::NAX: return std::make_unique(std::move(file)); // NX NSP (Nintendo Submission Package) file format case FileType::NSP: + case FileType::NSZ: return std::make_unique(std::move(file), system.GetFileSystemController(), system.GetContentProvider(), program_id, program_index); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 95ce638da0..d3c0230cb0 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -42,7 +42,9 @@ enum class FileType { NRO, NCA, NSP, + NSZ, XCI, + XCZ, NAX, KIP, DeconstructedRomDirectory, diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 0a3dc5885b..ea1d1c9ecd 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -7,12 +7,14 @@ #include #include #include +#include #include #include "common/common_funcs.h" #include "common/hex_util.h" #include "common/logging.h" #include "common/lz4_compression.h" +#include "common/zbic_compression.h" #include "common/settings.h" #include "common/swap.h" #include "core/core.h" @@ -104,11 +106,36 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset); if (nso_header.IsSegmentCompressed(i)) { - int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]); - ASSERT(r == int(nso_header.segments[i].size)); - std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size); + if (nso_header.IsZBICCompressed()) { + // ZBIC compression + const int r = Common::Compression::DecompressDataZBIC( + std::span{decompressed_size}.first(nso_header.segments[i].size), + std::span{compressed_data}.first(nso_header.segments_compressed_size[i]) + ); + ASSERT(r > 0); + } else { + // LZ4 compression + int r = Common::Compression::DecompressDataLZ4( + decompressed_size.data(), + nso_header.segments[i].size, + compressed_data.data(), + nso_header.segments_compressed_size[i] + ); + ASSERT(r == int(nso_header.segments[i].size)); + } + + std::memcpy( + codeset.memory.data() + module_start + nso_header.segments[i].location, + decompressed_size.data(), + nso_header.segments[i].size + ); } else { - std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size); + // Not compressed + std::memcpy( + codeset.memory.data() + module_start + nso_header.segments[i].location, + compressed_data.data(), + nso_header.segments[i].size + ); } codeset.segments[i].addr = module_start + nso_header.segments[i].location; codeset.segments[i].offset = module_start + nso_header.segments[i].location; diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 6356697e33..4a373edd07 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -58,6 +61,9 @@ struct NSOHeader { std::array segment_hashes; bool IsSegmentCompressed(size_t segment_num) const; + bool IsZBICCompressed() const { + return ((flags >> 7) & 1) != 0; + } }; static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size."); static_assert(std::is_trivially_copyable_v, "NSOHeader must be trivially copyable."); diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index cfc908b4ef..8b2c79394d 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -56,6 +56,17 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, AppLoader_NSP::~AppLoader_NSP() = default; FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) { + // .nsz is NSP-compatible; return NSZ so the game list shows the right type. + if (nsp_file->GetName().ends_with(".nsz")) { + return FileType::NSZ; + } + + // PFS0-shaped .xcz containers are handled by this loader too; report XCZ so the game + // list does not show them as NSP. XCI-shaped .xcz is identified by AppLoader_XCI. + if (nsp_file->GetName().ends_with(".xcz")) { + return FileType::XCZ; + } + const FileSys::NSP nsp(nsp_file); if (nsp.GetStatus() != ResultStatus::Success) { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a5e6b8592c..eabc6dfccb 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -101,8 +101,10 @@ struct Memory::Impl { } u64 protect_bytes = 0, protect_begin = 0; + + current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS); for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) { - const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type(); + const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type(); switch (page_type) { case Common::PageType::RasterizerCachedMemory: if (protect_bytes > 0) { @@ -123,16 +125,14 @@ struct Memory::Impl { } [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { - Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr; - if (paddr) - return system.DeviceMemory().GetPointer(paddr + vaddr); + if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr) + return reinterpret_cast(paddr) + vaddr; return {}; } [[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const { - const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr; - if (paddr != 0) - return system.DeviceMemory().GetPointer(paddr + vaddr); + if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr) + return reinterpret_cast(paddr) + vaddr; return {}; } @@ -243,10 +243,12 @@ struct Memory::Impl { std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_offset = addr & YUZU_PAGEMASK; bool user_accessible = true; + + current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1); while (remaining_size != 0) { const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size); const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset); - const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType(); + const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock(); switch (type) { case Common::PageType::Unmapped: { user_accessible = false; @@ -297,10 +299,10 @@ struct Memory::Impl { } [[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept { - return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr; + return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr; } [[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept { - return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr; + return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr; } bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) { @@ -404,11 +406,14 @@ struct Memory::Impl { // The region is at a granularity of CPU pages. const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + + current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages); for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { - const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type(); + auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS); + const auto [pointer, type, block] = entry.PointerTypeBlock(true); if (debug) { // Switch page type to debug if now debug - switch (page_type) { + switch (type) { case Common::PageType::Unmapped: ASSERT(false && "Attempted to mark unmapped pages as debug"); break; @@ -417,14 +422,14 @@ struct Memory::Impl { // Page is already marked. break; case Common::PageType::Memory: - current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory); + entry.MarkDebug(pointer, block); break; default: UNREACHABLE(); } } else { // Switch page type to non-debug if now non-debug - switch (page_type) { + switch (type) { case Common::PageType::Unmapped: ASSERT(false && "Attempted to mark unmapped pages as non-debug"); break; @@ -433,8 +438,7 @@ struct Memory::Impl { // Don't mess with already non-debug or rasterizer memory. break; case Common::PageType::DebugMemory: { - u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK); - current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); + entry.Store(false, Common::PageType::Memory, block, pointer); break; } default: @@ -466,8 +470,10 @@ struct Memory::Impl { // is different). This assumes the specified GPU address region is contiguous as well. const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages); for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { - const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type(); + auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS); + const Common::PageType page_type = entry.Type(); if (cached) { // Switch page type to cached if now cached switch (page_type) { @@ -477,7 +483,7 @@ struct Memory::Impl { break; case Common::PageType::DebugMemory: case Common::PageType::Memory: - current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory); + entry.MarkRasterizerCached(); break; case Common::PageType::RasterizerCachedMemory: // There can be more than one GPU region mapped per CPU region, so it's common @@ -499,13 +505,13 @@ struct Memory::Impl { // that this area is already unmarked as cached. break; case Common::PageType::RasterizerCachedMemory: { - if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) { + if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) { // It's possible that this function has been called while updating the // pagetable after unmapping a VMA. In that case the underlying VMA will no // longer exist, and we should just leave the pagetable entry blank. - current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped); + entry.Store(false, Common::PageType::Unmapped, block, 0); } else { - current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); + entry.Store(false, Common::PageType::Memory, block, ptr); } break; } @@ -539,22 +545,18 @@ struct Memory::Impl { ASSERT_MSG(type != Common::PageType::Memory, "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); - while (base != end) { - page_table.entries[base].ptr.Store(0, type); - page_table.entries[base].addr = 0; - page_table.entries[base].block = 0; - base += 1; - } + page_table.entries.ZeroRegion(base, end); } else { - auto orig_base = base; - while (base != end) { - auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer(target)) - (base << YUZU_PAGEBITS); - auto backing = GetInteger(target) - (base << YUZU_PAGEBITS); - page_table.entries[base].ptr.Store(host_ptr, type); - page_table.entries[base].addr = backing; - page_table.entries[base].block = orig_base << YUZU_PAGEBITS; + auto current_block = block_count.fetch_add(1, std::memory_order_relaxed); + ASSERT(current_block != 65535); - ASSERT_MSG(page_table.entries[base].ptr.Pointer(), + page_table.entries.CommitRegion(base, end); + while (base != end) { + auto host_ptr = reinterpret_cast(system.DeviceMemory().GetPointer(target)) - (base << YUZU_PAGEBITS);; + auto& entry = page_table.entries.GetUnchecked(base); + + entry.Store(false, type, current_block, host_ptr); + ASSERT_MSG(page_table.entries[base].Pointer(), "memory mapping base yield a nullptr within the table"); base += 1; @@ -569,11 +571,11 @@ struct Memory::Impl { vaddr &= 0xffffffffffffULL; if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] { // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw(); - if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] { + const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw(); + if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] { return reinterpret_cast(pointer + vaddr); } else { - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { + switch (static_cast(raw.type)) { case Common::PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr); return nullptr; @@ -755,7 +757,7 @@ struct Memory::Impl { }; auto& gpu = system.GPU(); gpu_device_memory->ApplyOpOnPointer( - p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); }); + p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size, true); }); } Core::System& system; @@ -773,6 +775,7 @@ struct Memory::Impl { #else Common::HostMemory* host_buffer{}; #endif + std::atomic block_count = 0; }; Memory::Memory(Core::System& system_) : system{system_} { @@ -811,7 +814,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { if (page >= page_table.entries.size()) { return false; } - const auto [pointer, type] = page_table.entries[page].ptr.PointerType(); + const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock(); return pointer != 0 || type == Common::PageType::RasterizerCachedMemory || type == Common::PageType::DebugMemory; } diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 73a86b3c8c..606ac2da42 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) { switch (width) { - case 1: - memory.Write8(addr, static_cast(value)); - break; - case 2: - memory.Write16(addr, static_cast(value)); - break; - case 4: - memory.Write32(addr, static_cast(value)); - break; - case 8: - memory.Write64(addr, value); - break; + case sizeof(u64): return memory.Write64(addr, value); + case sizeof(u32): return memory.Write32(addr, u32(value)); + case sizeof(u16): return memory.Write16(addr, u16(value)); + case sizeof(u8): return memory.Write8(addr, u8(value)); default: UNREACHABLE(); } diff --git a/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp index da51220e9c..0cf9bb9fae 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -24,29 +24,65 @@ namespace Dynarmic::Backend::Arm64 { template -static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; - const auto info = Devirtualize(this_); - oaknut::Label l_addr, l_this; - void* target = code.xptr(); + // params = { this, vaddr, bitsize } code.LDR(X0, l_this); + // X1 = vaddr + code.MOV(X2, bitsize); code.LDR(Xscratch0, l_addr); code.BR(Xscratch0); - code.align(8); code.l(l_this); code.dx(info.this_ptr); code.l(l_addr); code.dx(info.fn_ptr); - return target; } template -static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { + using namespace oaknut::util; + const auto info = Devirtualize(this_); + oaknut::Label l_addr, l_this; + void* target = code.xptr(); + // params = { this, vaddr, value, bitsize } + code.LDR(X0, l_this); + // X1 = vaddr + // X2 = value + code.MOV(X3, bitsize); + code.LDR(Xscratch0, l_addr); + code.BR(Xscratch0); + code.align(8); + code.l(l_this); + code.dx(info.this_ptr); + code.l(l_addr); + code.dx(info.fn_ptr); + return target; +} + +template +static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { + using namespace oaknut::util; + const auto info = Devirtualize(this_); + oaknut::Label l_addr, l_this; + void* target = code.xptr(); + code.LDR(X0, l_this); + code.LDR(Xscratch0, l_addr); + code.BR(Xscratch0); + code.align(8); + code.l(l_this); + code.dx(info.this_ptr); + code.l(l_addr); + code.dx(info.fn_ptr); + return target; +} + +template +static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; const auto info = Devirtualize(this_); @@ -59,6 +95,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_ ABI_PushRegisters(code, save_regs, 0); code.LDR(X0, l_this); code.MOV(X1, Xscratch0); + code.MOV(X2, bitsize); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); code.MOV(Xscratch0, X0); @@ -82,7 +119,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T { return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); + return (conf.callbacks->*callback)(vaddr, sizeof(T)); }); }; @@ -101,7 +138,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const } template -static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; const auto info = Devirtualize(this_); @@ -115,6 +152,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this code.LDR(X0, l_this); code.MOV(X1, Xscratch0); code.MOV(X2, Xscratch1); + code.MOV(X3, bitsize); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); ABI_PopRegisters(code, save_regs, 0); @@ -136,12 +174,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const oaknut::Label l_addr, l_this; auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 { - return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, - [&](T expected) -> bool { - return (conf.callbacks->*callback)(vaddr, value, expected); - }) - ? 0 - : 1; + return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, [&](T expected) -> bool { + return (conf.callbacks->*callback)(vaddr, value, expected); + }) ? 0 : 1; }; void* target = code.xptr(); @@ -179,26 +214,27 @@ void A32AddressSpace::EmitPrelude() { UnprotectCodeMemory(); - prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); - prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); - prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf); - prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf); - prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf); - prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf); - prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); - prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + + prelude_info.read_memory_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8)); + prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16)); + prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32)); + prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64)); + prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8)); + prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16)); + prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32)); + prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64)); + prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf); + prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf); + prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf); + prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf); + prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8)); + prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16)); + prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32)); + prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64)); + prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8)); + prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16)); + prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32)); + prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64)); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf); @@ -371,8 +407,10 @@ EmitConfig A32AddressSpace::GetEmitConfig() { .page_table_pointer = std::bit_cast(conf.page_table), .page_table_address_space_bits = 32, - .page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits, + .page_table_pointer_mask = conf.page_table_pointer_mask, .page_table_log2_stride = conf.page_table_log2_stride, + .page_table_marked_bit = conf.page_table_marked_bit, + .page_table_sign_extension = conf.page_table_sign_extension, .silently_mirror_page_table = true, .absolute_offset_page_table = conf.absolute_offset_page_table, .detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table, diff --git a/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp b/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp index 2c71ffe282..492242fa73 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp @@ -23,29 +23,65 @@ namespace Dynarmic::Backend::Arm64 { template -static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; - const auto info = Devirtualize(this_); - oaknut::Label l_addr, l_this; - void* target = code.xptr(); + // params = { this, vaddr, bitsize } code.LDR(X0, l_this); + // X1 = vaddr + code.MOV(X2, bitsize); code.LDR(Xscratch0, l_addr); code.BR(Xscratch0); - code.align(8); code.l(l_this); code.dx(info.this_ptr); code.l(l_addr); code.dx(info.fn_ptr); - return target; } template -static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { + using namespace oaknut::util; + const auto info = Devirtualize(this_); + oaknut::Label l_addr, l_this; + void* target = code.xptr(); + // params = { this, vaddr, value, bitsize } + code.LDR(X0, l_this); + // X1 = vaddr + // X2 = value + code.MOV(X3, bitsize); + code.LDR(Xscratch0, l_addr); + code.BR(Xscratch0); + code.align(8); + code.l(l_this); + code.dx(info.this_ptr); + code.l(l_addr); + code.dx(info.fn_ptr); + return target; +} + +template +static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { + using namespace oaknut::util; + const auto info = Devirtualize(this_); + oaknut::Label l_addr, l_this; + void* target = code.xptr(); + code.LDR(X0, l_this); + code.LDR(Xscratch0, l_addr); + code.BR(Xscratch0); + code.align(8); + code.l(l_this); + code.dx(info.this_ptr); + code.l(l_addr); + code.dx(info.fn_ptr); + return target; +} + +template +static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; const auto info = Devirtualize(this_); @@ -58,6 +94,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_ ABI_PushRegisters(code, save_regs, 0); code.LDR(X0, l_this); code.MOV(X1, Xscratch0); + code.MOV(X2, bitsize); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); code.MOV(Xscratch0, X0); @@ -81,7 +118,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T { return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); + return (conf.callbacks->*callback)(vaddr, sizeof(T)); }); }; @@ -100,7 +137,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const } template -static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { +static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) { using namespace oaknut::util; const auto info = Devirtualize(this_); @@ -114,6 +151,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this code.LDR(X0, l_this); code.MOV(X1, Xscratch0); code.MOV(X2, Xscratch1); + code.MOV(X3, bitsize); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); ABI_PopRegisters(code, save_regs, 0); @@ -133,14 +171,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const using namespace oaknut::util; oaknut::Label l_addr, l_this; - auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 { - return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, - [&](T expected) -> bool { - return (conf.callbacks->*callback)(vaddr, value, expected); - }) - ? 0 - : 1; + return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, [&](T expected) -> bool { + return (conf.callbacks->*callback)(vaddr, value, expected); + }) ? 0 : 1; }; void* target = code.xptr(); @@ -346,30 +380,30 @@ void A64AddressSpace::EmitPrelude() { UnprotectCodeMemory(); - prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); + prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8)); + prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16)); + prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32)); + prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64)); prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks); - prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); + prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8)); + prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16)); + prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32)); + prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64)); prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks); - prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf); - prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf); - prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf); - prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf); + prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf); + prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf); + prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf); + prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf); prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf); - prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8)); + prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16)); + prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32)); + prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64)); prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks); - prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8)); + prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16)); + prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32)); + prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64)); prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); @@ -545,8 +579,10 @@ EmitConfig A64AddressSpace::GetEmitConfig() { .page_table_pointer = std::bit_cast(conf.page_table), .page_table_address_space_bits = conf.page_table_address_space_bits, - .page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits, + .page_table_pointer_mask = conf.page_table_pointer_mask, .page_table_log2_stride = conf.page_table_log2_stride, + .page_table_marked_bit = conf.page_table_marked_bit, + .page_table_sign_extension = conf.page_table_sign_extension, .silently_mirror_page_table = conf.silently_mirror_page_table, .absolute_offset_page_table = conf.absolute_offset_page_table, .detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table, diff --git a/src/dynarmic/src/dynarmic/backend/arm64/address_space.cpp b/src/dynarmic/src/dynarmic/backend/arm64/address_space.cpp index 947c50f4c3..4ef3296c4a 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/address_space.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/address_space.cpp @@ -145,6 +145,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::ReturnFromRunCode: c.B(prelude_info.return_from_run_code); break; + // { this, vaddr, size } case LinkTarget::ReadMemory8: c.BL(prelude_info.read_memory_8); break; @@ -160,6 +161,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::ReadMemory128: c.BL(prelude_info.read_memory_128); break; + // { this, vaddr, size } case LinkTarget::WrappedReadMemory8: c.BL(prelude_info.wrapped_read_memory_8); break; @@ -175,6 +177,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::WrappedReadMemory128: c.BL(prelude_info.wrapped_read_memory_128); break; + // case LinkTarget::ExclusiveReadMemory8: c.BL(prelude_info.exclusive_read_memory_8); break; @@ -190,6 +193,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::ExclusiveReadMemory128: c.BL(prelude_info.exclusive_read_memory_128); break; + // { this, vaddr, value, size } case LinkTarget::WriteMemory8: c.BL(prelude_info.write_memory_8); break; @@ -205,6 +209,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::WriteMemory128: c.BL(prelude_info.write_memory_128); break; + // case LinkTarget::WrappedWriteMemory8: c.BL(prelude_info.wrapped_write_memory_8); break; @@ -220,6 +225,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) { case LinkTarget::WrappedWriteMemory128: c.BL(prelude_info.wrapped_write_memory_128); break; + // case LinkTarget::ExclusiveWriteMemory8: c.BL(prelude_info.exclusive_write_memory_8); break; diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.h index f2c89bbfc0..693f71ea39 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.h @@ -128,8 +128,10 @@ struct EmitConfig { // Page table u64 page_table_pointer; std::size_t page_table_address_space_bits; - int page_table_pointer_mask_bits; + u64 page_table_pointer_mask; std::size_t page_table_log2_stride; + std::optional page_table_marked_bit; + std::optional page_table_sign_extension; bool silently_mirror_page_table; bool absolute_offset_page_table; u8 detect_misaligned_access_via_page_table; diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.cpp b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.cpp index 143244d60b..170fcf9613 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_memory.cpp @@ -273,9 +273,18 @@ std::pair InlinePageTableEmitVAddrLookup(oaknut::Cod // load x0 = *<(u8*)pagetable + index> code.LDR(Xscratch0, Xpagetable, Xscratch0); - if (ctx.conf.page_table_pointer_mask_bits != 0) { - const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits; - code.AND(Xscratch0, Xscratch0, mask); + if (ctx.conf.page_table_marked_bit) { + code.TST(Xscratch0, 1ULL << *ctx.conf.page_table_marked_bit); + code.B(NE, *fallback); + } + + if (ctx.conf.page_table_pointer_mask != 0) { + code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask); + } + + // TODO: combine this with page_table_pointer_mask + if (ctx.conf.page_table_sign_extension) { + code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension); } code.CBZ(Xscratch0, *fallback); diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp index f138b5f137..e866b2acd6 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp @@ -31,16 +31,16 @@ using namespace Xbyak::util; void A32EmitX64::GenFastmemFallbacks() { const std::initializer_list idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; const std::array, 4> read_callbacks{{ - {8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)}, - {16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)}, - {32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)}, - {64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)}, + {8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, }}; const std::array, 4> write_callbacks{{ - {8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)}, - {16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)}, - {32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)}, - {64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)}, + {8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, }}; const std::array, 4> exclusive_write_callbacks{{ {8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, @@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() { code.align(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); + // params = { this, vaddr, size } if (vaddr_idx != code.ABI_PARAM2.getIdx()) { code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); } - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT); + if (ordered) code.mfence(); callback.EmitCall(code); if (value_idx != code.ABI_RETURN.getIdx()) { code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); @@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() { code.align(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(code); + // params = { this, vaddr, value, size } if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { @@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() { } } code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); + code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT); callback.EmitCall(code); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); ABI_PopCallerSaveRegistersAndAdjustStack(code); code.ret(); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize)); @@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() { #undef Axx void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst); + EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst); + EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst); + EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) { @@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) { void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp index 4b7054b4b2..8bb2fcfa59 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp @@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() { void A64EmitX64::GenFastmemFallbacks() { const std::initializer_list idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; const std::array, 4> read_callbacks{{ - {8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)}, - {16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)}, - {32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)}, - {64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)}, + {8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, }}; const std::array, 4> write_callbacks{{ - {8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)}, - {16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)}, - {32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)}, - {64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)}, + {8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, }}; const std::array, 4> exclusive_write_callbacks{{ {8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, @@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() { code.align(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); + // params = { this, vaddr, size } if (vaddr_idx != code.ABI_PARAM2.getIdx()) { code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); } - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT); + if (ordered) code.mfence(); callback.EmitCall(code); if (value_idx != code.ABI_RETURN.getIdx()) { code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); @@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() { code.align(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(code); + // params = { this, vaddr, value, size } if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { @@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() { } } code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); + code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT); callback.EmitCall(code); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); ABI_PopCallerSaveRegistersAndAdjustStack(code); code.ret(); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize)); @@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() { #undef Axx void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { @@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { } void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst); + EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst); + EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst); + EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { @@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index 4fa14d504b..8bb8be3d5b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { // Neither fastmem nor page table: Use callbacks if constexpr (bitsize == 128) { ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); code.CallFunction(memory_read_128); ctx.reg_alloc.DefineValue(code, inst, xmm1); } else { ctx.reg_alloc.HostCall(code, inst, {}, args[1]); - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT); + if (ordered) code.mfence(); Devirtualize(conf.callbacks).EmitCall(code); code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } @@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.HostCall(code, nullptr); code.CallFunction(memory_write_128); } else { + // { this, vaddr, value, size } ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]); + code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT); Devirtualize(conf.callbacks).EmitCall(code); } - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); EmitCheckMemoryAbort(ctx, inst); return; } @@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { if (ordered) { code.mfence(); } - code.CallLambda( - [](AxxUserConfig& conf, Axx::VAddr vaddr) -> T { - return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); - }); + code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T { + return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { + return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT); }); + }); code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } else { const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code); @@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { if (ordered) { code.mfence(); } - code.CallLambda( - [](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) { - ret = conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> Vector { - return (conf.callbacks->*callback)(vaddr); - }); + code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) { + ret = conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> Vector { + return (conf.callbacks->*callback)(vaddr); }); + }); code.movups(result, xword[rsp + ABI_SHADOW_SPACE]); ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h index 3ac078f1d7..d072b9963d 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/a32_emit_x64.h" @@ -78,27 +79,46 @@ Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, EmitContext& ctx, size_t bitsiz template<> [[maybe_unused]] Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) { const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code); - const Xbyak::Reg32 tmp = ctx.conf.absolute_offset_page_table ? page.cvt32() : ctx.reg_alloc.ScratchGpr(code).cvt32(); + const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code); - EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp.cvt64()); + EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp); - // TODO: This code assumes vaddr has been zext from 32-bits to 64-bits. - - code.mov(tmp, vaddr.cvt32()); + code.mov(tmp, vaddr); code.shr(tmp, int(page_table_const_bits)); - code.shl(tmp, int(ctx.conf.page_table_log2_stride)); - code.mov(page, qword[r14 + tmp.cvt64()]); - if (ctx.conf.page_table_pointer_mask_bits == 0) { - code.test(page, page); + + if (ctx.conf.page_table_log2_stride > 3) { + code.shl(tmp, int(ctx.conf.page_table_log2_stride)); + code.mov(page, qword[r14 + tmp.cvt64()]); } else { - code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits); + code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]); } + + // check for marked bit, use as unmapped if marked + if (ctx.conf.page_table_marked_bit) { + code.bt(page, *ctx.conf.page_table_marked_bit); + code.jc(abort, code.T_NEAR); + } + // mask away attributes + if (ctx.conf.page_table_pointer_mask == 0) { + code.test(page, page); + } else if (std::in_range(ctx.conf.page_table_pointer_mask)) { + code.and_(page, ctx.conf.page_table_pointer_mask); + } else { + code.mov(tmp, ctx.conf.page_table_pointer_mask); + code.and_(page, tmp); + } + // check for sign bit, apply sign extension as needed + if (ctx.conf.page_table_sign_extension) { + code.shl(page, 63 - int(*ctx.conf.page_table_sign_extension)); + code.sar(page, 63 - int(*ctx.conf.page_table_sign_extension)); + } + code.jz(abort, code.T_NEAR); if (ctx.conf.absolute_offset_page_table) { return page + vaddr; } - code.mov(tmp, vaddr.cvt32()); - code.and_(tmp, static_cast(page_table_const_mask)); + code.mov(tmp, vaddr); + code.and_(tmp, u32(page_table_const_mask)); return page + tmp.cvt64(); } @@ -108,7 +128,7 @@ template<> const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits; const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code); - const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table ? page : ctx.reg_alloc.ScratchGpr(code); + const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code); EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp); @@ -141,13 +161,33 @@ template<> code.jnz(abort, code.T_NEAR); } - code.shl(tmp, int(ctx.conf.page_table_log2_stride)); - code.mov(page, qword[r14 + tmp]); - if (ctx.conf.page_table_pointer_mask_bits == 0) { - code.test(page, page); + if (ctx.conf.page_table_log2_stride > 3) { + code.shl(tmp, int(ctx.conf.page_table_log2_stride)); + code.mov(page, qword[r14 + tmp.cvt64()]); } else { - code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits); + code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]); } + + // check for marked bit, use as unmapped if marked + if (ctx.conf.page_table_marked_bit) { + code.bt(page, *ctx.conf.page_table_marked_bit); + code.jc(abort, code.T_NEAR); + } + // mask away attributes + if (ctx.conf.page_table_pointer_mask == 0) { + code.test(page, page); + } else if (std::in_range(ctx.conf.page_table_pointer_mask)) { + code.and_(page, ctx.conf.page_table_pointer_mask); + } else { + code.mov(tmp, ctx.conf.page_table_pointer_mask); + code.and_(page, tmp); + } + // check for sign bit, apply sign extension as needed + if (ctx.conf.page_table_sign_extension) { + code.shl(page, *ctx.conf.page_table_sign_extension); + code.sar(page, *ctx.conf.page_table_sign_extension); + } + code.jz(abort, code.T_NEAR); if (ctx.conf.absolute_offset_page_table) { return page + vaddr; diff --git a/src/dynarmic/src/dynarmic/interface/A32/config.h b/src/dynarmic/src/dynarmic/interface/A32/config.h index 5a97fb69f3..49063ed619 100644 --- a/src/dynarmic/src/dynarmic/interface/A32/config.h +++ b/src/dynarmic/src/dynarmic/interface/A32/config.h @@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks { // All reads through this callback are 4-byte aligned. // Memory must be interpreted as little endian. - std::optional MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); } + std::optional MemoryReadCode(VAddr vaddr) override { + return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t))); + } // This function is called before the instruction at pc is read. // IR code can be emitted by the callee prior to instruction handling. @@ -80,16 +82,10 @@ struct UserCallbacks : public TranslateCallbacks { // Reads through these callbacks may not be aligned. // Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT. - virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; - virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0; - virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0; - virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0; + virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0; // Writes through these callbacks may not be aligned. - virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; - virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0; - virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0; - virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0; + virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0; // Writes through these callbacks may not be aligned. virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; } @@ -159,14 +155,23 @@ struct UserConfig { /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Masks out the first N bits in host pointers from the page table. + /// Applies a bit mask to the bits in host pointers from the page table. /// The intention behind this is to allow users of Dynarmic to pack attributes in the /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - std::int32_t page_table_pointer_mask_bits = 0; + /// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes. + std::uint64_t page_table_pointer_mask = 0; - // Log2 of the size per page entry, value should be either 3 or 4 - std::size_t page_table_log2_stride = 3; + /// Log2 of the size per page entry, value should be either 3 or 4 + std::uint32_t page_table_log2_stride = 3; + + /// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table. + /// If the bit is set to 1, Dynarmic will treat it as unmapped. + /// This bit should be included as part of `page_table_pointer_mask_bits`. + std::optional page_table_marked_bit = std::nullopt; + + /// If this value is set, Dynarmic will sign extend the page table pointer by this bit. + /// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`. + std::optional page_table_sign_extension = std::nullopt; /// Select the architecture version to use. /// There are minor behavioural differences between versions. diff --git a/src/dynarmic/src/dynarmic/interface/A64/config.h b/src/dynarmic/src/dynarmic/interface/A64/config.h index 83c1593fd8..859aeba709 100644 --- a/src/dynarmic/src/dynarmic/interface/A64/config.h +++ b/src/dynarmic/src/dynarmic/interface/A64/config.h @@ -89,20 +89,16 @@ struct UserCallbacks { // All reads through this callback are 4-byte aligned. // Memory must be interpreted as little endian. - virtual std::optional MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); } + virtual std::optional MemoryReadCode(VAddr vaddr) { + return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t))); + } // Reads through these callbacks may not be aligned. - virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; - virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0; - virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0; - virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0; + virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0; virtual Vector MemoryRead128(VAddr vaddr) = 0; // Writes through these callbacks may not be aligned. - virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; - virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0; - virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0; - virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0; + virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0; virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0; // Writes through these callbacks may not be aligned. @@ -173,14 +169,23 @@ struct UserConfig { /// This is only used if page_table is not nullptr. std::uint32_t page_table_address_space_bits = 36; - /// Masks out the first N bits in host pointers from the page table. + /// Applies a bit mask to the bits in host pointers from the page table. /// The intention behind this is to allow users of Dynarmic to pack attributes in the /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - std::int32_t page_table_pointer_mask_bits = 0; + /// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes. + std::uint64_t page_table_pointer_mask = 0; - // Log2 of the size per page entry, value should be either 3 or 4 - std::size_t page_table_log2_stride = 3; + /// Log2 of the size per page entry, value should be either 3 or 4 + std::uint32_t page_table_log2_stride = 3; + + /// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table. + /// If the bit is set to 1, Dynarmic will treat it as unmapped. + /// This bit should be included as part of `page_table_pointer_mask`. + std::optional page_table_marked_bit = std::nullopt; + + /// If this value is set, Dynarmic will sign extend the page table pointer by this bit. + /// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`. + std::optional page_table_sign_extension = std::nullopt; /// Counter-timer frequency register. The value of the register is not interpreted by /// dynarmic. diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index 02b8e821da..b32bbd517a 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u8 value_from_memory = cb->MemoryRead8(vaddr); + const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u16 value_from_memory = cb->MemoryRead16(vaddr); + const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u32 value_from_memory = cb->MemoryRead32(vaddr); + const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u64 value_from_memory = cb->MemoryRead64(vaddr); + const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } diff --git a/src/dynarmic/tests/A32/fuzz_thumb.cpp b/src/dynarmic/tests/A32/fuzz_thumb.cpp index 67a01daf9c..a29b877f3e 100644 --- a/src/dynarmic/tests/A32/fuzz_thumb.cpp +++ b/src/dynarmic/tests/A32/fuzz_thumb.cpp @@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") { } } -TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") { +TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") { ThumbTestEnv test_env; // Prepare test subjects diff --git a/src/dynarmic/tests/A32/test_arm_instructions.cpp b/src/dynarmic/tests/A32/test_arm_instructions.cpp index 5a27cd499c..ee24131386 100644 --- a/src/dynarmic/tests/A32/test_arm_instructions.cpp +++ b/src/dynarmic/tests/A32/test_arm_instructions.cpp @@ -553,9 +553,9 @@ TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") { memset(backing_memory, 0, memory_size); memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57); - env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0] - env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1] - env.MemoryWrite32(8, 0xEAFFFFFE); // B . + env.MemoryWrite(0, 0xE5904000, sizeof(u32)); // LDR R4, [R0] + env.MemoryWrite(4, 0xE5814000, sizeof(u32)); // STR R4, [R1] + env.MemoryWrite(8, 0xEAFFFFFE, sizeof(u32)); // B . jit.Regs()[0] = 0x100; jit.Regs()[1] = 0x1F0; jit.Regs()[15] = 0; // PC = 0 diff --git a/src/dynarmic/tests/A32/testenv.h b/src/dynarmic/tests/A32/testenv.h index bebc2566da..a22cc7131e 100644 --- a/src/dynarmic/tests/A32/testenv.h +++ b/src/dynarmic/tests/A32/testenv.h @@ -16,6 +16,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/interface/A32/a32.h" template @@ -59,42 +60,51 @@ public: return infinite_loop_u32; // B . } - std::uint8_t MemoryRead8(u32 vaddr) override { - if (IsInCodeMem(vaddr)) { - return reinterpret_cast(code_mem.data())[vaddr]; + u64 MemoryRead(u32 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (IsInCodeMem(vaddr)) + return reinterpret_cast(code_mem.data())[vaddr]; + if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) + return iter->second; + return u8(vaddr); } - if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) { - return iter->second; + default: + std::abort(); } - return static_cast(vaddr); - } - std::uint16_t MemoryRead16(u32 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u32 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u32 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; } - void MemoryWrite8(u32 vaddr, std::uint8_t value) override { - if (vaddr < code_mem.size() * sizeof(u32)) { - code_mem_modified_by_guest = true; + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + if (vaddr < code_mem.size() * sizeof(u32)) + code_mem_modified_by_guest = true; + modified_memory[vaddr] = value; + break; + default: + std::abort(); } - modified_memory[vaddr] = value; - } - void MemoryWrite16(u32 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, static_cast(value)); - MemoryWrite8(vaddr + 1, static_cast(value >> 8)); - } - void MemoryWrite32(u32 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, static_cast(value)); - MemoryWrite16(vaddr + 2, static_cast(value >> 16)); - } - void MemoryWrite64(u32 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, static_cast(value)); - MemoryWrite32(vaddr + 4, static_cast(value >> 32)); } void CallSVC(std::uint32_t swi) override { @@ -143,46 +153,41 @@ public: return read(vaddr); } - std::uint8_t MemoryRead8(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint16_t MemoryRead16(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint32_t MemoryRead32(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint64_t MemoryRead64(std::uint32_t vaddr) override { - return read(vaddr); + u64 MemoryRead(u32 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return read(vaddr); + case sizeof(u32): return read(vaddr); + case sizeof(u16): return read(vaddr); + case sizeof(u8): return read(vaddr); + default: + std::abort(); + } } - void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override { - write(vaddr, value); - } - void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override { - write(vaddr, value); - } - void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override { - write(vaddr, value); - } - void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override { - write(vaddr, value); + void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override { + switch (size) { + case sizeof(u64): return write(vaddr, u64(value)); + case sizeof(u32): return write(vaddr, u32(value)); + case sizeof(u16): return write(vaddr, u16(value)); + case sizeof(u8): return write(vaddr, u8(value)); + default: std::abort(); + } } - bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { + MemoryWrite(vaddr, value, sizeof(u8)); return true; } - bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { + MemoryWrite(vaddr, value, sizeof(u16)); return true; } - bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { + MemoryWrite(vaddr, value, sizeof(u32)); return true; } - bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { + MemoryWrite(vaddr, value, sizeof(u64)); return true; } diff --git a/src/dynarmic/tests/A64/a64.cpp b/src/dynarmic/tests/A64/a64.cpp index 99cc9d3d4d..24ddfd4084 100644 --- a/src/dynarmic/tests/A64/a64.cpp +++ b/src/dynarmic/tests/A64/a64.cpp @@ -1006,8 +1006,8 @@ TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") { REQUIRE(jit.GetRegister(1) == 0x7f7e7d7c7b7a7978); REQUIRE(jit.GetRegister(2) == 0x8786858483828180); REQUIRE(jit.GetRegister(4) == 0); - REQUIRE(env.MemoryRead64(0x1234567812345678) == 0xaf00d1e5badcafe0); - REQUIRE(env.MemoryRead64(0x1234567812345680) == 0xd0d0cacad0d0caca); + REQUIRE(env.MemoryRead(0x1234567812345678, sizeof(u64)) == 0xaf00d1e5badcafe0); + REQUIRE(env.MemoryRead(0x1234567812345680, sizeof(u64)) == 0xd0d0cacad0d0caca); } TEST_CASE("A64: CNTPCT_EL0", "[a64]") { @@ -1517,11 +1517,11 @@ TEST_CASE("A64: Memory access (fastmem)", "[a64]") { memset(backing_memory, 0, memory_size); memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57); - env.MemoryWrite32(0, 0xA9401404); // LDP X4, X5, [X0] - env.MemoryWrite32(4, 0xF9400046); // LDR X6, [X2] - env.MemoryWrite32(8, 0xA9001424); // STP X4, X5, [X1] - env.MemoryWrite32(12, 0xF9000066); // STR X6, [X3] - env.MemoryWrite32(16, 0x14000000); // B . + env.MemoryWrite(0, 0xA9401404, sizeof(u32)); // LDP X4, X5, [X0] + env.MemoryWrite(4, 0xF9400046, sizeof(u32)); // LDR X6, [X2] + env.MemoryWrite(8, 0xA9001424, sizeof(u32)); // STP X4, X5, [X1] + env.MemoryWrite(12, 0xF9000066, sizeof(u32)); // STR X6, [X3] + env.MemoryWrite(16, 0x14000000, sizeof(u32)); // B . jit.SetRegister(0, 0x100); jit.SetRegister(1, 0x1F0); jit.SetRegister(2, 0x10F); @@ -1788,8 +1788,8 @@ TEST_CASE("A64: rand3", "[a64]") { memset(backing_memory, 0, memory_size); // cat rand2.txt | awk '{print "env.code_mem.emplace_back(0x"$2"); // "$0}' > rand2-out.tx - env.MemoryWrite32(100, 0x58028edd); // 0000000000000084 58028edd ldr x29, #20952 - env.MemoryWrite32(104, 0x14000000); // 0000000000000ea4 14000000 b #0 + env.MemoryWrite(100, 0x58028edd, sizeof(u32)); // 0000000000000084 58028edd ldr x29, #20952 + env.MemoryWrite(104, 0x14000000, sizeof(u32)); // 0000000000000ea4 14000000 b #0 jit.SetPC(100); jit.SetPstate(0xb0000000); @@ -1824,7 +1824,7 @@ TEST_CASE("A64: rand2", "[a64][.]") { // cat rand2.txt | awk '{print "env.code_mem.emplace_back(0x"$2"); // "$0}' > rand2-out.tx const std::array code32 = {0xea80f352, 0x6e65e59d, 0x1e20c343, 0x2e3a7192, 0x2e267249, 0xd500405f, 0x6f01f461, 0x6eb684fc, 0x58028edd, 0x0ea5f5b6, 0x0ea069fb, 0x2e769517, 0x5e066063, 0x1e65c3f5, 0x4f00ff52, 0x93401cf6, 0x1e274248, 0x6f67aaf5, 0x5e0c0782, 0x5ef43f3c, 0x2e6595b7, 0x4e20590f, 0xb35aa451, 0x6ee2c5ed, 0x4e32bf46, 0x2ea1ba8f, 0x2f68a85e, 0x9237d90a, 0x5e23dd10, 0x0e762e32, 0x4e31a8cf, 0xce1f3360, 0x781a4ac0, 0x13834066, 0x5fa8101c, 0x6f7c5594, 0x0e71bb68, 0xbc0b3e8f, 0x785dbbda, 0x6f51e794, 0xce50af75, 0x1ad728ec, 0x6ee0da4c, 0xb84efa14, 0x2eb3f613, 0x4e287ade, 0x4eb8c734, 0x2e83f4e8, 0x0e397c80, 0xd08f93f8, 0xce718e48, 0x0f672a0d, 0x2e9edd40, 0x0e14128b, 0x6f5942e6, 0x8b3a0f03, 0x3c5d16b9, 0x7f7e3743, 0x4f4c54e4, 0x0ea0a9e9, 0x9e59dbe6, 0x6e7ddcd3, 0xcec08377, 0x9ba759f8, 0x2ea5046e, 0x0e24c569, 0xb8979780, 0x4e31b98c, 0x4efe4f46, 0x4ea7c762, 0x7e61c9c6, 0x6e30c880, 0x1ada0c25, 0x4e603a2f, 0xda9d7218, 0x0d40c5d9, 0x5e214b05, 0x9ba9efc5, 0x5e61b81e, 0x6e7bc31c, 0x0e61a163, 0x9e5832d2, 0x4e772248, 0x4e3d17c8, 0x92624f60, 0x7a1a02dc, 0x79891f65, 0x6eb45036, 0x0e321ee8, 0x4e2566f0, 0x4ea02b9b, 0x0f9dcb3d, 0x2e21b9f9, 0x0e21a8c3, 0xda1700bd, 0x6ea0fb38, 0x7e607a0b, 0x72845817, 0x7f61068e, 0x0d60e529, 0x4ea0ca5c, 0x1a94b20f, 0x8b87419d, 0x7ea9ed71, 0x2ea1a86e, 0x4d40c4da, 0x5ea0eada, 0x784ba96e, 0x7eb6ee02, 0x3db1c710, 0x0e217836, 0x7ee0bb96, 0x4e786c08, 0x4e976a08, 0x489ffe86, 0x4e79fc9b, 0x0e21cbce, 0x5ef7fc65, 0x4ea1286d, 0xd29c771e, 0x6f5c2839, 0x0ea00a9d, 0x6ee44c06, 0x5ee1d858, 0x5ef2fda6, 0x7eb0c9fe, 0x7f762791, 0x2e212ae6, 0x4e61c9db, 0x13003c57, 0x5ee1b8f8, 0x0f2396d2, 0x6ea0db1e, 0x0e71ba82, 0xab29c807, 0x6ef8f8b3, 0x1f18d4a1, 0x0e261d15, 0x1e290081, 0x1b0c7d12, 0x4e7771c3, 0xf845f1e4, 0x4d40c9e8, 0xce778452, 0x6eb9879d, 0x6e21c93d, 0xcec0829f, 0x52a0969f, 0x1e772b4f, 0x7ee1da88, 0x5f52fe0a, 0x7f3387b1, 0x5e214850, 0x1e65c025, 0x0e2ca294, 0x2e614829, 0x1e640077, 0x9e240048, 0x4ebe9537, 0x9bb7925e, 0x38b669c5, 0x2840d089, 0x6f43e648, 0x2e662d28, 0x4eabaff3, 0x6e734cc7, 0x0e31baee, 0x7ee0d93c, 0x5e282bde, 0x7e21bba4, 0x4e6c75fa, 0x5ac01217, 0x7f4304af, 0x1e7878ed, 0x1ada2196, 0x7ee1aba3, 0x93407f3c, 0x4f6c34eb, 0x6e3447a9, 0x7e7ae545, 0x5e0802bb, 0x6eeae63a, 0x7ee1da62, 0x5e280bb3, 0xf81d4009, 0x1e603b21, 0x5e281a14, 0x6eb0a99b, 0x1e266a25, 0x0d60cafe, 0x0e0b6194, 0x7a4ed2c5, 0x92b762ec, 0x4e6b5749, 0x3c16a6e5, 0x4ea0a92b, 0x0fa58b6a, 0x5f76148c, 0x6e30c95f, 0x1e6540fd, 0x5e28e40f, 0x0d403fd4, 0x7e30da36, 0x7fda9b51, 0x2ea04bde, 0x1e25c3d2, 0x1ee0434c, 0x5e21d8e7, 0x5ee1ba51, 0x5e61aba9, 0x4e2849fb, 0x5ee098ea, 0x4e60f63d, 0x0f280443, 0x5ee0da27, 0x2e78a6ce, 0x78054afc, 0x4e14286b, 0x4e218bd8, 0x2a3d2551, 0x3a04017a, 0x5f4317cd, 0x0e604a37, 0x9a834614, 0x0e2edf4d, 0x7a51a0a0, 0x5f8e9043, 0x6ea06bb2, 0xaa2857dd, 0x7a1903fc, 0x301ba9ba, 0x9ac929cd, 0x4e061ff0, 0x2e38fcfc, 0x0e2f614a, 0x7ee0d8e4, 0x6e73afda, 0x7f4156f7, 0x0e6078bf, 0x4ee1d9ed, 0x93403fbe, 0xce6f8640, 0x4e3855e3, 0x6f76fe23, 0x112466e8, 0x1e358a90, 0x7f45272c, 0x6ea19a9d, 0x8a696350, 0x1e3900f6, 0x5e61c866, 0x0e3fbfd0, 0x5ee09ad0, 0x0e651d27, 0x4dffc35e, 0x2e20c6ce, 0x0fbe118d, 0x1e656a15, 0xd1357365, 0x0e20a847, 0xce4a835c, 0x4e203905, 0x2e60090d, 0x7f4a27bb, 0x1e64c316, 0xce7d86a4, 0x7ebded2d, 0x6e70a97e, 0x4eb9a42b, 0x0e209bef, 0x6f151730, 0x0e7e30f7, 0x4e724509, 0xd503375f, 0xce58b6ae, 0x5e21a9b8, 0xcb2ca538, 0x5ac01131, 0x6ea19a24, 0xeb40c8b3, 0xc8df7d65, 0x78108341, 0x3218ab9b, 0x0f3da7dd, 0x2e003089, 0x4e21cab5, 0x8aa5c924, 0x1a94950c, 0x123e506f, 0x13117e37, 0x1ee6005b, 0x5ac00647, 0x5eec8cd5, 0x7ef0fb3d, 0x9223272a, 0x5ee0cb02, 0x6e66071d, 0x6ea1dbbf, 0x5e61c903, 0x5ac015ea, 0x93db6206, 0x7e62b5e3, 0x6ea0c87b, 0xdac0090e, 0x48df7d90, 0x6e206ba5, 0x9e2503c2, 0x6e25fc89, 0x4d60e2db, 0x1e3e22a0, 0x2eb81c19, 0x7856ea00, 0x5fbfb22d, 0x1e630244, 0x4e202a83, 0x1f50a722, 0x7f7b55d2, 0x0fae89b9, 0x4e781d73, 0xce738c3a, 0x4f15a591, 0x6e21c7e1, 0x586ff77e, 0x8a5d3592, 0x93401c67, 0x5e61cb86, 0xce6bc2c1, 0x6e393f10, 0x9bb70ec3, 0xdac0098c, 0x4da84b95, 0x7f494476, 0x9ace5c11, 0x7e61ca14, 0x4f7a60ef, 0x1ad32b39, 0x0ea3777f, 0x5e61da7f, 0x4f1404e2, 0x4e3244e2, 0x6e1b1ceb, 0x0dee5aac, 0x4e2f9dc4, 0x5ea1b8c3, 0x1e59f863, 0xd500403f, 0x4e3ae7d0, 0x4ef5c6ea, 0x08dffe3b, 0x6e36f4f6, 0x2e764f29, 0x0e726f23, 0x5f42375b, 0x7f71fc40, 0x6e618aad, 0x93403e5b, 0x0e205976, 0x0e7250c4, 0x6eb0abc9, 0x2e2049f0, 0x5f14754d, 0x7f6ce468, 0x6f950bbe, 0x6e31aa47, 0x4eb83396, 0x0dccc952, 0x2ea1ca90, 0xce69c701, 0xb0bed69e, 0x7c5dec39, 0x4e2868a2, 0x0e591b08, 0x5f34e6dd, 0x3a449184, 0x5e3ce6de, 0x4ea149b7, 0x4e7ad29b, 0xba198503, 0x1f683e8f, 0xfa52f2a7, 0x6e30dffc, 0x4e6c3d17, 0x2eae3248, 0xd503349f, 0x1e60002c, 0x0f180680, 0x9e240049, 0x6f75774e, 0xa90d8678, 0x9ad924c4, 0x7eb0f85b, 0x0e205aaf, 0x7ee08899, 0x5f4bffd8, 0x1b0ff5f3, 0x4ee11dcd, 0x2e218948, 0x0dcb2733, 0x4eac107c, 0x4ea04a53, 0x4e287b44, 0x0e60b82a, 0x5ee0ebbc, 0xce454ff1, 0x5e1761e7, 0x5e09202f, 0x0e0c0754, 0x1e72e6b9, 0x7e21da70, 0x0fbdb20c, 0x5efb8c84, 0xd500401f, 0x3a47526e, 0x1e680acf, 0x7f7375fc, 0xf80522da, 0x4ee60c02, 0x4d40c2e7, 0x6f89096b, 0x7ee1bb6e, 0x5e280b4a, 0x1e3120c8, 0x7eb2ef96, 0x4fd012dd, 0x0f3027ef, 0x4e2078a8, 0xd503201f, 0x2e2312d9, 0x6ebf1c6e, 0x5ee1f8df, 0x4e607a46, 0x6e30c877, 0x6c09d2d1, 0x4e61abd8, 0x0e35267e, 0x6ac17728, 0x0e861aa0, 0x6f63fe26, 0x6f157628, 0x6f30a5f9, 0x4d60cc0c, 0x4e21cb59, 0x2e68a3fb, 0x7efae601, 0x6ea0f82c, 0x9b25ec12, 0x1a1a0305, 0x0e043fe1, 0x6e73c0ed, 0x6ea1b8c0, 0x7e20380b, 0x0f0534e8, 0x1f56bc7d, 0xba0c0128, 0x1e672160, 0x6e7b259b, 0x7ee07b5d, 0x9a820443, 0x4e040581, 0x2f1d87e8, 0x1acd2f5b, 0x6e20794f, 0x2e6a3c93, 0xc8dffe13, 0xce5ab1c6, 0x6eea55f6, 0x4ea039b3, 0x0d602fec, 0x2e246e2f, 0x7857be39, 0xb80608fb, 0x1e67c017, 0x9bcf7f63, 0x0f92d857, 0x5e0812f7, 0x1e210172, 0x7e6128e9, 0x7ea94d41, 0x981179e1, 0x1effb018, 0x2e600828, 0x0eb9c6b2, 0x6ee1baae, 0x4ea0db28, 0x2ea1487b, 0x4ea6c7f0, 0x2e2374c7, 0x7e30d8dd, 0xb9991fa7, 0x4e791e3e, 0x889f7c4b, 0x0e6c753c, 0x1e740ad1, 0x1e244324, 0x1ef33010, 0x5ac01102, 0x9bd97fba, 0x6e290143, 0x1e2220d8, 0x4d8d5aee, 0x6f28570b, 0xfa4ab0c1, 0xdac00b14, 0x7ea1a90e, 0x2e3027d8, 0x6f25a733, 0x4e61a96e, 0x4e1a2fcb, 0x0e22fe0a, 0xc8df7cd0, 0x5e280a55, 0x4e012b20, 0x7e70dbf4, 0x520c5a4e, 0x6ea6c57f, 0x0e861af8, 0xd503233f, 0x889ffe3c, 0x5e274ea9, 0x4e21a89a, 0x0e170c02, 0x6efd4c0b, 0xd5033ebf, 0x6e61a92c, 0x2e205b72, 0x789fb828, 0x0e626e94, 0x2ea6724c, 0x9a10028b, 0x2c6c51fc, 0x5a9de6b9, 0x6e6881f3, 0x5ee0ea6b, 0x0faec36e, 0x0e955bca, 0x1acf206d, 0x7f6f571b, 0x4e286930, 0x12b41ceb, 0x1e770b7a, 0x0ea18ac2, 0x5e282aaf, 0xf2b7fa1e, 0x1ac34311, 0x13167d11, 0x4ea63412, 0x6e758038, 0x2f1d85d6, 0x0f275480, 0x0ead6c71, 0x6e204b69, 0x1e6303f4, 0x5e0031ef, 0x13001e40, 0x7a16006f, 0x6e6ae4c0, 0x0f0f242f, 0x6e674f50, 0x4e606b7a, 0x7e6ee684, 0x1e6b5957, 0x7ea1bbab, 0x7ea0b6cb, 0xce4da241, 0x0ea1b953, 0x0eb2af4b, 0x9ac309d0, 0x6e61d8bd, 0x5ea0d890, 0x5f47d1e7, 0xfa5981ca, 0x1e7f7959, 0x6ef24dd8, 0x0e0a41d1, 0x5ee0e898, 0x4e6038e2, 0x13097d65, 0x6f839088, 0x9e290265, 0x0e208824, 0x2e65af79, 0x6f36a561, 0x9ad3204b, 0x0e21482e, 0x1e24431d, 0xd50330bf, 0x0df641aa, 0x6e602a83, 0xce30505f, 0x5e025238, 0xd503201f, 0x4e608880, 0x4de9c38d, 0x5e0f5348, 0x6eb48ca9, 0x50fda31b, 0x2e251eec, 0x7842ba50, 0xd8a1cd86, 0x2ea09862, 0x0ea09983, 0x2ea333b0, 0x0ea6032c, 0x4f94801b, 0x7e3ee57d, 0x38135e4f, 0xd8fdd9dd, 0x5ee0fcde, 0x9e64033d, 0x6e37f547, 0x6e3dd7ef, 0x13003f3d, 0x0e602f9f, 0x4e7ad014, 0x9b3b6857, 0x5ea0cb67, 0x0eb31c9f, 0x4e7c5372, 0x5e61b8c0, 0x0ea19b23, 0x0ee6e1df, 0x6e63a626, 0x2f139405, 0x7eb0f96d, 0x9e588c63, 0x2e714c3a, 0x6e8c941e, 0x0f61b331, 0x6f01f625, 0x4e78d4ea, 0x6f403709, 0x1a0300da, 0xda0102c8, 0x7e61d9fd, 0xb89469bb, 0x0c838780, 0x2e60a590, 0x4dfd29e1, 0x4e150f2e, 0xce2810bc, 0x5f541591, 0x9ee60259, 0x2eb40e56, 0x5e014027, 0x2ef71faf, 0x4e2d452f, 0x5ee0a813, 0x4eb03301, 0x38443acf, 0x6eabd502, 0x0e2ee71e, 0x5a960364, 0xce7ec596, 0x7efbed09, 0x4ef42ea2, 0x0eb30ea5, 0x5ee0d9f8, 0x6f513552, 0xf89eb3fa, 0x7ea2eca6, 0x9b00cc19, 0xf897409e, 0x1e73485f, 0x381afa77, 0x0f169f3b, 0x5ee1aa70, 0x5e1803ee, 0x0dbf5a4c, 0xce78c7a6, 0x9b0b260c, 0x2ef8fa19, 0x6e70aa4b, 0xce45b805, 0x2ea08e86, 0x4ee0bafd, 0x2ea09a1f, 0x4e218900, 0x6e744f13, 0xce518653, 0xf81b7a68, 0xce45ac5e, 0x7e62e416, 0x1a1b02b6, 0x7e21db48, 0x381daaaf, 0x6b2c0987, 0x0e2ec651, 0x4eae8502, 0x9bde7ca0, 0x6f47201f, 0x7e61a8a3, 0x6e60d5db, 0x4e2879de, 0xf81d194e, 0x4f1b8d05, 0x4d0048b2, 0x6e203be9, 0x4e3e7eb1, 0x0e260ef8, 0x2e688518, 0x7e3fec46, 0xdac00843, 0xf85c8917, 0x2e212a0f, 0x0e8196da, 0xd503359f, 0xce4c81f2, 0x6ee19992, 0x6e21ca79, 0x4d40c1d2, 0x4f5816ef, 0x4e34c3ea, 0x4df7c283, 0x7ef7eeb6, 0x18e276ce, 0xab0d21c0, 0xd5032f7f, 0x4ea00dbf, 0x5ac01251, 0xd0121955, 0x7f1495e4, 0x7ef0fa11, 0x5e24dd9c, 0x9add25b5, 0x0eb2bdef, 0x9e1977c7, 0x6f4b26bd, 0x0e200a9c, 0x9b4f7c00, 0x0ea0392e, 0x7e212a2c, 0x0b248b90, 0x1acc27a1, 0x2e701c90, 0x5ee1b870, 0x5e280aba, 0x5ea0780e, 0x1e264246, 0x4e052d04, 0x0e731dc4, 0xce461997, 0x9a9e9413, 0x3d462048, 0x5ea1fac5, 0x2ea0c8c4, 0x9a030280, 0x2ebda4b8, 0x5eef8614, 0x6eadc4e0, 0xbd035a8f, 0x4e606b84, 0x4eb1aba1, 0x4e286928, 0x4e2858cc, 0x9add0ce9, 0x4e070d65, 0x5fd399d5, 0x0f03fde7, 0x6ee90c74, 0x4ef8e31e, 0x381d986a, 0x5ea0ebf4, 0x5ea0d87e, 0x2e76ac9e, 0x6eb36cd4, 0x2e6e1c4c, 0x2e2feebc, 0x1ace4b03, 0x5ee0db12, 0x5ea0e9b1, 0x2e1c32d5, 0x5fa49a09, 0x0e258737, 0x7e21ca8e, 0xce4f9988, 0x5f7f56a6, 0x0e739766, 0x4e28586c, 0x6e619908, 0xd500401f, 0xf88b9252, 0x6e251c8e, 0x9e20015b, 0x7f1486b9, 0x717c339b, 0x1f31ff70, 0x4ea0eb62, 0x9acb0926, 0x489f7d85, 0x4e209b54, 0x2e84cf03, 0x2e65946c, 0x0e7d80cd, 0xc8dffecc, 0xce668bd8, 0x6e2188af, 0xeb4ada34, 0x2b25ec33, 0x0d40e6e7, 0x4eb2c757, 0x4ec82ad0, 0x7e21cb0a, 0x0e21a847, 0x4e0b1ec0, 0x381e6ac0, 0x6e61c8f5, 0x0f10071c, 0x2ee21daa, 0x5e61ab31, 0x6e218892, 0x2e7e7cb5, 0x6f2826aa, 0x7f6b54df, 0x4eaa2620, 0xdac00034, 0x4f6477be, 0x7e6148ea, 0x4eef1f57, 0x78459aeb, 0x2ebc3f10, 0x2e35f4eb, 0x4fbf19ce, 0xd8d0e58e, 0x2e21bbc7, 0x6ee0cab6, 0x9bc57e3f, 0x2f854037, 0x4e92181c, 0x6e6d1f89, 0x0f305545, 0x4ee19a57, 0x0e887bdf, 0x5e1a4185, 0x7ef0c821, 0x2eb6607c, 0x2ea0d9b8, 0x9e0380f4, 0x2ebf1c83, 0x1e62597d, 0x7f6e2548, 0x5ac00205, 0x4e616adb, 0xce638b8c, 0x5e1653cf, 0x2e6069be, 0x0e2ac641, 0x1e33c76f, 0xce44956d, 0x9bb90d31, 0x1e24c20a, 0x7ee038c1, 0x93407e5e, 0x4e280127, 0xc8df7f7d, 0xba42f263, 0x1e6f199c, 0x6e212889, 0x6e92f60e, 0x6ebdc499, 0x8b9acbf8, 0x4d40c581, 0x3a020250, 0x6e6a6716, 0x9248403b, 0x9081ffea, 0x4e603856, 0x9ad1242b, 0x6f270579, 0x1a070349, 0xcec08133, 0xd503305f, 0x5a1a00ca, 0x2e60b8a2, 0x0e5f28fd, 0x0e31a3da, 0x7e61cbc1, 0xd503399f, 0x5f5e54aa, 0x0eb8bdea, 0x4eba8f10, 0x4e2a2e60, 0x2f3da7d6, 0x1e58e297, 0x6e71aa3e, 0x6b86701a, 0xce4fa5e6, 0x4ee7c463, 0x8a79307f, 0x0ebea541, 0x2e218af4, 0x4e774f8a, 0xb9b95dc5, 0x6e61abd5, 0x4dd1e814, 0x4da72098, 0x98307582, 0x3a512101, 0x7ef95497, 0x1ace5535, 0x5a0c0349, 0x4e28581b, 0x6ebf1c02, 0x5ea1da23, 0x1e274314, 0x5e25dd29, 0x6e75f594, 0x6eaf6ed5, 0x4e214abe, 0x4e064172, 0x2e21c8f4, 0xf84c5b08, 0x1e244312, 0x14000000}; for (size_t i = 0; i < code32.size(); ++i) - env.MemoryWrite32(100 + i, code32[i]); + env.MemoryWrite(100 + i, code32[i], sizeof(u32)); env.ignore_invalid_insn = true; jit.SetRegister(0, 0x866524401a1d4e47); diff --git a/src/dynarmic/tests/A64/fibonacci.cpp b/src/dynarmic/tests/A64/fibonacci.cpp index f52a04e217..8816a89b1c 100644 --- a/src/dynarmic/tests/A64/fibonacci.cpp +++ b/src/dynarmic/tests/A64/fibonacci.cpp @@ -25,48 +25,55 @@ public: u64 ticks_left = 0; ::Common::unordered_map memory{}; - u8 MemoryRead8(u64 vaddr) override { - return memory[vaddr]; - } - - u16 MemoryRead16(u64 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - - u32 MemoryRead32(u64 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - - u64 MemoryRead64(u64 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): + return memory[vaddr]; + default: + std::abort(); + } } std::array MemoryRead128(u64 vaddr) override { - return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)}; + return { + MemoryRead(vaddr, sizeof(u64)), + MemoryRead(vaddr + sizeof(u64), sizeof(u64)) + }; } - void MemoryWrite8(u64 vaddr, u8 value) override { - memory[vaddr] = value; + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + memory[vaddr] = value; + break; + default: + std::abort(); + } } - - void MemoryWrite16(u64 vaddr, u16 value) override { - MemoryWrite8(vaddr, u8(value)); - MemoryWrite8(vaddr + 1, u8(value >> 8)); - } - - void MemoryWrite32(u64 vaddr, u32 value) override { - MemoryWrite16(vaddr, u16(value)); - MemoryWrite16(vaddr + 2, u16(value >> 16)); - } - - void MemoryWrite64(u64 vaddr, u64 value) override { - MemoryWrite32(vaddr, u32(value)); - MemoryWrite32(vaddr + 4, u32(value >> 32)); - } - void MemoryWrite128(u64 vaddr, std::array value) override { - MemoryWrite64(vaddr, value[0]); - MemoryWrite64(vaddr + 8, value[1]); + MemoryWrite(vaddr, value[0], sizeof(u64)); + MemoryWrite(vaddr + 8, value[1], sizeof(u64)); } void CallSVC(u32) override { @@ -135,9 +142,9 @@ TEST_CASE("A64: fibonacci", "[a64]") { code.RET(); for (size_t i = 0; i < 1024; i++) { - env.MemoryWrite32(i * 4, instructions[i]); + env.MemoryWrite(i * 4, instructions[i], sizeof(u32)); } - env.MemoryWrite32(8888, 0xd4200000); + env.MemoryWrite(8888, 0xd4200000, sizeof(u32)); cpu.SetRegister(30, 8888); cpu.SetRegister(0, 10); diff --git a/src/dynarmic/tests/A64/testenv.h b/src/dynarmic/tests/A64/testenv.h index f4ddc63a6b..11aa6389f8 100644 --- a/src/dynarmic/tests/A64/testenv.h +++ b/src/dynarmic/tests/A64/testenv.h @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "dynarmic/interface/A64/a64.h" +#include "dynarmic/interface/A64/config.h" using Vector = Dynarmic::A64::Vector; @@ -34,64 +35,79 @@ public: return code_mem[index]; } - std::uint8_t MemoryRead8(u64 vaddr) override { - if (IsInCodeMem(vaddr)) { - return reinterpret_cast(code_mem.data())[vaddr - code_mem_start_address]; + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (IsInCodeMem(vaddr)) + return reinterpret_cast(code_mem.data())[vaddr - code_mem_start_address]; + if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) + return it->second; + return u8(vaddr); + } + default: + std::abort(); } - if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) - return it->second; - return u8(vaddr); - } - std::uint16_t MemoryRead16(u64 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u64 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u64 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; - } - Vector MemoryRead128(u64 vaddr) override { - return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)}; } - void MemoryWrite8(u64 vaddr, std::uint8_t value) override { - if (IsInCodeMem(vaddr)) { - code_mem_modified_by_guest = true; + Vector MemoryRead128(u64 vaddr) override { + return { + MemoryRead(vaddr, sizeof(u64)), + MemoryRead(vaddr + 8, sizeof(u64)) + }; + } + + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + if (IsInCodeMem(vaddr)) { + code_mem_modified_by_guest = true; + } + modified_memory[vaddr] = value; + break; + default: + std::abort(); } - modified_memory[vaddr] = value; - } - void MemoryWrite16(u64 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, u8(value)); - MemoryWrite8(vaddr + 1, u8(value >> 8)); - } - void MemoryWrite32(u64 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, u16(value)); - MemoryWrite16(vaddr + 2, u16(value >> 16)); - } - void MemoryWrite64(u64 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, u32(value)); - MemoryWrite32(vaddr + 4, u32(value >> 32)); } void MemoryWrite128(u64 vaddr, Vector value) override { - MemoryWrite64(vaddr, value[0]); - MemoryWrite64(vaddr + 8, value[1]); + MemoryWrite(vaddr, value[0], sizeof(u64)); + MemoryWrite(vaddr + 8, value[1], sizeof(u64)); } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u8)); return true; } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u16)); return true; } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u32)); return true; } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u64)); return true; } bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { @@ -145,52 +161,46 @@ public: return read(vaddr); } - std::uint8_t MemoryRead8(u64 vaddr) override { - return read(vaddr); - } - std::uint16_t MemoryRead16(u64 vaddr) override { - return read(vaddr); - } - std::uint32_t MemoryRead32(u64 vaddr) override { - return read(vaddr); - } - std::uint64_t MemoryRead64(u64 vaddr) override { - return read(vaddr); + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return read(vaddr); + case sizeof(u32): return read(vaddr); + case sizeof(u16): return read(vaddr); + case sizeof(u8): return read(vaddr); + default: std::abort(); + } } Vector MemoryRead128(u64 vaddr) override { return read(vaddr); } - void MemoryWrite8(u64 vaddr, std::uint8_t value) override { - write(vaddr, value); - } - void MemoryWrite16(u64 vaddr, std::uint16_t value) override { - write(vaddr, value); - } - void MemoryWrite32(u64 vaddr, std::uint32_t value) override { - write(vaddr, value); - } - void MemoryWrite64(u64 vaddr, std::uint64_t value) override { - write(vaddr, value); + void MemoryWrite(u64 vaddr, std::uint64_t value, size_t size) override { + switch (size) { + case sizeof(u64): return write(vaddr, u64(value)); + case sizeof(u32): return write(vaddr, u32(value)); + case sizeof(u16): return write(vaddr, u16(value)); + case sizeof(u8): return write(vaddr, u8(value)); + default: std::abort(); + } } void MemoryWrite128(u64 vaddr, Vector value) override { write(vaddr, value); } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u8)); return true; } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u16)); return true; } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u32)); return true; } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u64)); return true; } bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { diff --git a/src/dynarmic/tests/print_info.cpp b/src/dynarmic/tests/print_info.cpp index 5b1c5cfe63..87f4438ad4 100644 --- a/src/dynarmic/tests/print_info.cpp +++ b/src/dynarmic/tests/print_info.cpp @@ -18,6 +18,7 @@ #include #include #include +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/mcl/bit.hpp" #include "common/common_types.h" @@ -118,36 +119,47 @@ public: u64 ticks_left = 0; std::map memory; - std::uint8_t MemoryRead8(u32 vaddr) override { - if (auto iter = memory.find(vaddr); iter != memory.end()) { - return iter->second; + u64 MemoryRead(Dynarmic::A32::VAddr vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (auto const it = memory.find(vaddr); it != memory.end()) + return it->second; + return 0; + } + default: + std::abort(); } - return 0; - } - std::uint16_t MemoryRead16(u32 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u32 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u32 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; } - void MemoryWrite8(u32 vaddr, std::uint8_t value) override { - memory[vaddr] = value; - } - void MemoryWrite16(u32 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, static_cast(value)); - MemoryWrite8(vaddr + 1, static_cast(value >> 8)); - } - void MemoryWrite32(u32 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, static_cast(value)); - MemoryWrite16(vaddr + 2, static_cast(value >> 16)); - } - void MemoryWrite64(u32 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, static_cast(value)); - MemoryWrite32(vaddr + 4, static_cast(value >> 32)); + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + memory[vaddr] = value; + break; + default: + std::abort(); + } } void CallSVC(std::uint32_t swi) override { @@ -231,7 +243,7 @@ void ExecuteA32Instruction(u32 instruction) { if (const auto address = get_value()) { fmt::print("value: "); if (const auto value = get_value()) { - env.MemoryWrite32(*address, *value); + env.MemoryWrite(*address, *value, sizeof(u32)); fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value); } } @@ -247,8 +259,8 @@ void ExecuteA32Instruction(u32 instruction) { cpu.SetFpscr(fpscr); const u32 initial_pc = regs[15]; - env.MemoryWrite32(initial_pc + 0, instruction); - env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0 + env.MemoryWrite(initial_pc + 0, instruction, sizeof(u32)); + env.MemoryWrite(initial_pc + 4, 0xEAFFFFFE, sizeof(u32)); // B +0 cpu.Run(); fmt::print("{}", fmt::join(cpu.Disassemble(), "\n")); diff --git a/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp b/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp index c0e1e13d9b..1fe09cdef0 100644 --- a/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp +++ b/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp @@ -290,7 +290,7 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type auto page = std::make_unique(); page->address = base_address; for (size_t i = 0; i < page->data.size(); ++i) - page->data[i] = this_->testenv.MemoryRead8(static_cast(base_address + i)); + page->data[i] = u8(this_->testenv.MemoryRead(u32(base_address + i), sizeof(u8))); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); if (err == UC_ERR_MAP) @@ -321,24 +321,7 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type template bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) { auto* this_ = static_cast(user_data); - - switch (size) { - case 1: - this_->testenv.MemoryWrite8(start_address, static_cast(value)); - break; - case 2: - this_->testenv.MemoryWrite16(start_address, static_cast(value)); - break; - case 4: - this_->testenv.MemoryWrite32(start_address, static_cast(value)); - break; - case 8: - this_->testenv.MemoryWrite64(start_address, value); - break; - default: - UNREACHABLE(); - } - + this_->testenv.MemoryWrite(start_address, value, size); return true; } diff --git a/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp b/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp index c9a194d50a..4afb6a6859 100644 --- a/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp +++ b/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp @@ -197,7 +197,7 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta auto page = std::make_unique(); page->address = base_address; for (size_t i = 0; i < page->data.size(); ++i) - page->data[i] = this_->testenv.MemoryRead8(base_address + i); + page->data[i] = u8(this_->testenv.MemoryRead(base_address + i, sizeof(u8))); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); if (err == UC_ERR_MAP) @@ -227,23 +227,6 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { auto* this_ = static_cast(user_data); - - switch (size) { - case 1: - this_->testenv.MemoryWrite8(start_address, static_cast(value)); - break; - case 2: - this_->testenv.MemoryWrite16(start_address, static_cast(value)); - break; - case 4: - this_->testenv.MemoryWrite32(start_address, static_cast(value)); - break; - case 8: - this_->testenv.MemoryWrite64(start_address, value); - break; - default: - UNREACHABLE(); - } - + this_->testenv.MemoryWrite(start_address, value, size); return true; } diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index 4f3112e3fb..6d79098efa 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2024 yuzu Emulator Project @@ -163,7 +163,9 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf std::shared_ptr nsp; FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::OpenMode::Read); - if (boost::to_lower_copy(file->GetName()).ends_with("nsp")) { + const auto lower_name = boost::to_lower_copy(file->GetName()); + + if (lower_name.ends_with("nsp") || lower_name.ends_with("nsz")) { nsp = std::make_shared(file); if (nsp->IsExtractedType()) { return InstallResult::Failure; diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp index 1092970ee8..7b4af13232 100644 --- a/src/hid_core/frontend/emulated_controller.cpp +++ b/src/hid_core/frontend/emulated_controller.cpp @@ -761,9 +761,6 @@ void EmulatedController::StartMotionCalibration() { } void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) { - const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); - const auto& player = Settings::values.players.GetValue()[player_index]; - if (index >= controller.button_values.size()) { return; } @@ -916,21 +913,10 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback break; } - if (!is_connected) { - if (npad_type == NpadStyleIndex::Handheld) { - if (npad_id_type == NpadIdType::Handheld) { - Connect(); - controller_connected[player_index] = true; - } - } else if (npad_type != NpadStyleIndex::Handheld) { - if (npad_id_type == NpadIdType::Player1) { - Connect(); - controller_connected[player_index] = true; - } else if (player.connected && !controller_connected[player_index]) { - Connect(); - controller_connected[player_index] = true; - } - } + const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); + const auto& player = Settings::values.players.GetValue()[player_index]; + if (player.connected) { + Connect(); } TriggerOnChange(ControllerTriggerType::Button, true); diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h index 7a2291b8a4..80ea633c63 100644 --- a/src/hid_core/frontend/emulated_controller.h +++ b/src/hid_core/frontend/emulated_controller.h @@ -22,7 +22,6 @@ #include "common/settings.h" #include "common/vector_math.h" #include "hid_core/frontend/motion_input.h" -#include "hid_core/hid_core.h" #include "hid_core/hid_types.h" #include "hid_core/irsensor/irs_types.h" @@ -585,7 +584,6 @@ private: std::array last_vibration_value{DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}; std::array last_vibration_timepoint{}; - std::array controller_connected{}; // Atomically synched values std::atomic npad_type{HID::NpadStyleIndex::None}; diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index e7a1ee2bf3..2b04e2c670 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -45,6 +45,19 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service AbstractPad{hid_core_.kernel}, }} { + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { + for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { + auto& controller = controller_data[aruid_index][i]; + controller.device = hid_core.GetEmulatedControllerByIndex(i); + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) { + ControllerUpdate(*kernel, type, i); + }, + .is_npad_service = true, + }; + controller.callback_key = controller.device->SetCallback(engine_callback); + } + } for (std::size_t i = 0; i < abstracted_pads.size(); ++i) { abstracted_pads[i].SetNpadId(IndexToNpadIdType(i)); } @@ -93,16 +106,6 @@ Result NPad::Activate(u64 aruid) { for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { auto& controller = controller_data[aruid_index][i]; controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; - controller.device = hid_core.GetEmulatedControllerByIndex(i); - if (!controller.callback_key) { - Core::HID::ControllerUpdateCallback engine_callback{ - .on_change = [this, i](Core::HID::ControllerTriggerType type) { - ControllerUpdate(hid_core.kernel, type, i); - }, - .is_npad_service = true, - }; - controller.callback_key = controller.device->SetCallback(engine_callback); - } } // Prefill controller buffers @@ -398,21 +401,21 @@ void NPad::InitNewlyAddedController(Kernel::KernelCore& kernel, u64 aruid, Core: void NPad::WriteEmptyEntry(NpadInternalState* npad) { NPadGenericState dummy_pad_state{}; NpadGcTriggerState dummy_gc_state{}; - dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->handheld_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->palma_lifo.WriteNextEntry(dummy_pad_state); - dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); - dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; + dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); } diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h index 68b803c3d3..2f7ac2d4f7 100644 --- a/src/hid_core/resources/npad/npad_types.h +++ b/src/hid_core/resources/npad/npad_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -6,6 +6,8 @@ #pragma once +#include + #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index fb2d75e384..ded92d3f1d 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 91589063b5..c3e9a066b3 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -697,16 +697,6 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_AddEventWatch(&SDLEventWatcher, this); initialized = true; - if (start_thread) { - vibration_thread = std::thread([this] { - Common::SetCurrentThreadName("SDL_Vibration"); - using namespace std::chrono_literals; - while (initialized) { - SendVibrations(); - std::this_thread::sleep_for(250ms); // 4 TPS - } - }); - } // Because the events for joystick connection happens before we have our event watcher added, we // can just open all the joysticks right here int joystick_count = 0; @@ -725,7 +715,6 @@ SDLDriver::~SDLDriver() { initialized = false; if (start_thread) { - vibration_thread.join(); SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD); } } @@ -805,10 +794,7 @@ Common::Input::DriverResult SDLDriver::SetVibration( .type = Common::Input::VibrationAmplificationType::Exponential, }; - vibration_queue.Push(VibrationRequest{ - .identifier = identifier, - .vibration = new_vibration, - }); + joystick->RumblePlay(new_vibration); return Common::Input::DriverResult::Success; } @@ -852,31 +838,6 @@ bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { return true; } -void SDLDriver::SendVibrations() { - std::vector filtered_vibrations{}; - while (!vibration_queue.Empty()) { - VibrationRequest request; - vibration_queue.Pop(request); - const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(), - static_cast(request.identifier.port)); - const auto it = std::find_if(filtered_vibrations.begin(), filtered_vibrations.end(), - [request](VibrationRequest vibration) { - return vibration.identifier == request.identifier; - }); - if (it == filtered_vibrations.end()) { - filtered_vibrations.push_back(std::move(request)); - continue; - } - *it = request; - } - - for (const auto& vibration : filtered_vibrations) { - const auto joystick = GetSDLJoystickByGUID(vibration.identifier.guid.RawString(), - static_cast(vibration.identifier.port)); - joystick->RumblePlay(vibration.vibration); - } -} - Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, const Common::UUID& guid, s32 axis, float value) const { Common::ParamPackage params{}; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 9a279e926f..e6751b6688 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -113,16 +113,11 @@ private: /// Returns true if the button is on the left joycon bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; - /// Queue of vibration request to controllers - Common::SPSCQueue vibration_queue; - /// Map of GUID of a list of corresponding virtual Joysticks ::Common::unordered_map>> joystick_map; std::mutex joystick_map_mutex; - bool start_thread = false; std::atomic initialized = false; - - std::thread vibration_thread; + bool start_thread; }; } // namespace InputCommon diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index dd3bd4398c..8597d02701 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -13,6 +13,8 @@ #include #include +#include + #include #include "common/bit_field.h" diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index 2384981f50..07e599f89e 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -4,7 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging.h" +#include #include "input_common/helpers/joycon_protocol/poller.h" namespace InputCommon::Joycon { diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 0562749073..bb92d896f8 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -154,25 +154,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) { INSERT(Settings, post_shader_preset, QString(), QString()); INSERT(Settings, post_shader_enabled, tr("Enable post-processing effects"), tr("Applies post-processing effects to the final image.")); - INSERT(Settings, frame_gen, tr("Frame generation (Lossless Scaling)"), - tr("Generates intermediate frames with the Lossless Scaling frame generation " - "shaders.\nRequires your own legal copy of Lossless.dll and forces VSync (FIFO).")); - INSERT(Settings, frame_gen_multiplier, tr("Frame generation multiplier:"), - tr("How many frames are shown for each frame the game renders.\nOnly used when the " - "target frame rate is 0.")); - INSERT(Settings, frame_gen_target_rate, tr("Frame generation target rate:"), - tr("Pick the rate your display can actually show. The multiplier then rises or falls " - "on its own to hold it.\n0 uses the fixed multiplier.")); - INSERT(Settings, frame_gen_flow_scale_auto, tr("Match motion estimation to the game"), - tr("Estimate motion at the resolution the game actually renders instead of the " - "upscaled output.")); - INSERT(Settings, frame_gen_flow_scale, tr("Motion estimation resolution:"), - tr("Resolution of the optical flow pass, as a fraction of the game's render " - "resolution.\nLowering it is the cheapest way to reclaim performance.")); - INSERT(Settings, frame_gen_queue_target, tr("Frame generation queue target:"), - tr("How many finished frames may wait ahead of the display.\nLarger queues absorb GPU " - "spikes at the cost of input latency.")); - INSERT(Settings, frame_gen_dump_flow, QString(), QString()); INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), tr("The method used to render the window in fullscreen.\nBorderless offers the best " "compatibility with the on-screen keyboard that some games request for " @@ -198,6 +179,14 @@ std::unique_ptr InitializeTranslations(QObject* parent) { "GPU: Use the GPU's compute shaders to decode ASTC textures (recommended).\n" "CPU Asynchronously: Use the CPU to decode ASTC textures on demand. Eliminates" "ASTC decoding\nstuttering but may present artifacts.")); + INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), + tr("Most GPUs lack support for ASTC textures and must decompress to an" + "intermediate format: RGBA8.\n" + "BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format,\n" + " saving VRAM but degrading image quality.")); + INSERT(Settings, frame_pacing_mode, tr("Frame Pacing Mode (Vulkan only)"), + tr("Controls how the emulator manages frame pacing to reduce stuttering and make the " + "frame rate smoother and more consistent.")); INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"), tr("Selects whether the emulator should prefer to conserve memory or make maximum usage " "of available video memory for performance.\nAggressive mode may impact performance " @@ -222,10 +211,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) { tr("Ensures data consistency between compute and memory operations.\nThis option fixes " "issues in games, but may degrade performance.\nUnreal Engine 4 games often see the " "most significant changes thereof.")); - INSERT(Settings, stall_on_gpu_fence_wait, tr("Pause CPU on GPU fence timeouts"), - tr("When a game keeps timing out while waiting for the GPU, pauses every CPU thread " - "until the GPU catches up.\nDisabling it keeps the other threads running, which can " - "reduce stutter but may break games that rely on it.")); INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), tr("Slightly improves performance by moving presentation to a separate CPU thread.")); INSERT( @@ -241,14 +226,29 @@ std::unique_ptr InitializeTranslations(QObject* parent) { INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"), tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting.")); INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"), - tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting.")); - INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"), - tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly.")); + tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting.")); INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"), tr("May reduce shader stutter.")); INSERT(Settings, gpu_clock, tr("GPU Clocks"), tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering " "resolution and render distance to fit the Switch's clocks.")); + INSERT(Settings, gpu_unswizzle_enabled, tr("GPU Unswizzle"), + tr("Accelerates BCn 3D texture decoding using GPU compute.\n" + "Disable if experiencing crashes or graphical glitches.")); + INSERT(Settings, gpu_unswizzle_texture_size, tr("GPU Unswizzle Max Texture Size"), + tr("Sets the maximum size (MiB) for GPU-based texture unswizzling.\n" + "While the GPU is faster for medium and large textures, the CPU may be more " + "efficient for very small ones.\n" + "Adjust this to find the balance between GPU acceleration and CPU overhead.")); + INSERT(Settings, gpu_unswizzle_stream_size, tr("GPU Unswizzle Stream Size"), + tr("Sets the maximum amount of texture data (in MiB) processed per frame.\n" + "Higher values can reduce stutter during texture loading but may impact frame " + "consistency.")); + INSERT(Settings, gpu_unswizzle_chunk_size, tr("GPU Unswizzle Chunk Size"), + tr("Determines the number of depth slices processed in a single dispatch.\n" + "Increasing this can improve throughput on high-end GPUs but may cause TDR or driver " + "timeouts on weaker hardware.")); + INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading " "time significantly in cases where the Vulkan driver does not store pipeline cache " @@ -271,12 +271,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) { "quality and performance consistency in some games.")); INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout.")); - INSERT(Settings, rescale_hack, tr("Enable Legacy Rescale Pass"), - tr("May fix rescale issues in some games by relying on behavior from the previous " - "implementation.\n" - "Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and " - "grey texture flicker on Nvidia GPUs in Luigis Mansion 3.")); - // Renderer (Extensions) INSERT(Settings, dyna_state, tr("Extended Dynamic State"), tr("Controls the number of features that can be used in Extended Dynamic State.\n" @@ -342,9 +336,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) { "it bypasses such prompts and directly exits the emulation.")); INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), tr("Hides the mouse after 2.5s of inactivity.")); - INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), - tr("Forcibly disables the use of the controller applet in emulated programs.\n" - "When a program attempts to open the controller applet, it is immediately closed.")); INSERT(UISettings, check_for_updates, tr("Check for updates"), tr("Whether or not to check for updates upon startup.")); @@ -380,6 +371,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { { PAIR(AppletMode, HLE, tr("Custom frontend")), PAIR(AppletMode, LLE, tr("Real applet")), + PAIR(AppletMode, Disabled, tr("Disabled")), }}); translations->insert({Settings::EnumMetadata::Index(), @@ -394,6 +386,21 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(AstcDecodeMode, Gpu, tr("GPU")), PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")), }}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")), + PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), + PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), + }}); + translations->insert({Settings::EnumMetadata::Index(), + { + PAIR(FramePacingMode, Target_Auto, tr("Auto")), + PAIR(FramePacingMode, Target_30, tr("30 FPS")), + PAIR(FramePacingMode, Target_60, tr("60 FPS")), + PAIR(FramePacingMode, Target_90, tr("90 FPS")), + PAIR(FramePacingMode, Target_120, tr("120 FPS")), + }}); translations->insert({Settings::EnumMetadata::Index(), { PAIR(VramUsageMode, Conservative, tr("Conservative")), @@ -425,7 +432,6 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(GpuFenceBehavior, Immediate, tr("Immediate")), PAIR(GpuFenceBehavior, Balanced, tr("Balanced")), PAIR(GpuFenceBehavior, Accurate, tr("Accurate")), - PAIR(GpuFenceBehavior, Strict, tr("Strict")), }}); translations->insert( {Settings::EnumMetadata::Index(), @@ -637,6 +643,30 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) { PAIR(GpuClock, Boost, tr("Boost")), PAIR(GpuClock, Overclock, tr("Overclock")), }}); + translations->insert({Settings::EnumMetadata::Index(), + { + PAIR(GpuUnswizzleSize, VerySmall, tr("Very Small (16 MB)")), + PAIR(GpuUnswizzleSize, Small, tr("Small (32 MB)")), + PAIR(GpuUnswizzleSize, Normal, tr("Normal (128 MB)")), + PAIR(GpuUnswizzleSize, Large, tr("Large (256 MB)")), + PAIR(GpuUnswizzleSize, VeryLarge, tr("Very Large (512 MB)")), + }}); + translations->insert({Settings::EnumMetadata::Index(), + { + PAIR(GpuUnswizzle, VeryLow, tr("Very Low (4 MB)")), + PAIR(GpuUnswizzle, Low, tr("Low (8 MB)")), + PAIR(GpuUnswizzle, Normal, tr("Normal (16 MB)")), + PAIR(GpuUnswizzle, Medium, tr("Medium (32 MB)")), + PAIR(GpuUnswizzle, High, tr("High (64 MB)")), + }}); + translations->insert({Settings::EnumMetadata::Index(), + { + PAIR(GpuUnswizzleChunk, VeryLow, tr("Very Low (32)")), + PAIR(GpuUnswizzleChunk, Low, tr("Low (64)")), + PAIR(GpuUnswizzleChunk, Normal, tr("Normal (128)")), + PAIR(GpuUnswizzleChunk, Medium, tr("Medium (256)")), + PAIR(GpuUnswizzleChunk, High, tr("High (512)")), + }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h index 58a538367a..a790cfcc22 100644 --- a/src/qt_common/config/uisettings.h +++ b/src/qt_common/config/uisettings.h @@ -127,8 +127,6 @@ struct Values { Setting hide_mouse{ linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, true, true}; - Setting controller_applet_disabled{linkage, false, "disableControllerApplet", - Category::UiGeneral}; // Set when Vulkan is known to crash the application bool has_broken_vulkan = false; @@ -262,5 +260,6 @@ Q_DECLARE_METATYPE(Settings::ResolutionSetup); Q_DECLARE_METATYPE(Settings::ScalingFilter); Q_DECLARE_METATYPE(Settings::AntiAliasing); Q_DECLARE_METATYPE(Settings::RendererBackend); +Q_DECLARE_METATYPE(Settings::AstcRecompression); Q_DECLARE_METATYPE(Settings::AstcDecodeMode); Q_DECLARE_METATYPE(Settings::SpirvOptimizeMode); diff --git a/src/qt_common/game_list/worker.cpp b/src/qt_common/game_list/worker.cpp index 586a26e8b9..6b6f93447d 100644 --- a/src/qt_common/game_list/worker.cpp +++ b/src/qt_common/game_list/worker.cpp @@ -360,7 +360,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa } if (target == ScanTarget::PopulateGameList && - (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP) && + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::XCZ || + file_type == Loader::FileType::NSP || file_type == Loader::FileType::NSZ) && !Loader::IsBootableGameContainer(file, file_type)) { return true; } @@ -375,7 +376,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa program_id, file); } else if (Settings::values.ext_content_from_game_dirs.GetValue() && (file_type == Loader::FileType::XCI || - file_type == Loader::FileType::NSP)) { + file_type == Loader::FileType::XCZ || + file_type == Loader::FileType::NSP || + file_type == Loader::FileType::NSZ)) { void(provider->AddEntriesFromContainer(file)); } } else { @@ -402,7 +405,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa }; if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 && - (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::XCZ || + file_type == Loader::FileType::NSP || file_type == Loader::FileType::NSZ)) { for (const auto id : program_ids) { // dravee suggested this, only viable way to // not show sub-games in qlaunch for now. diff --git a/src/qt_common/qt_common.cpp b/src/qt_common/qt_common.cpp index b9b8caf6e8..794aeaabb1 100644 --- a/src/qt_common/qt_common.cpp +++ b/src/qt_common/qt_common.cpp @@ -57,7 +57,8 @@ std::unique_ptr emu_thread = nullptr; const QStringList supported_file_extensions = {QStringLiteral("nro"), QStringLiteral("nso"), QStringLiteral("nca"), QStringLiteral("xci"), - QStringLiteral("nsp"), QStringLiteral("kip")}; + QStringLiteral("xcz"), QStringLiteral("nsp"), + QStringLiteral("nsz"), QStringLiteral("kip")}; Core::Frontend::WindowSystemType GetWindowSystemType() { // Determine WSI type based on Qt platform. diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index ee39ec2a47..9319ea007f 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -35,10 +32,10 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std std::string_view else_expr = {}) { const size_t num_buffers{ctx.info.storage_buffers_descriptors.size()}; for (size_t index = 0; index < num_buffers; ++index) { - const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; - if (!ssbo.is_global_fallback) { + if (!ctx.info.nvn_buffer_used[index]) { continue; } + const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)}; ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask @@ -68,10 +65,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std if (!else_expr.empty()) { ctx.Add("{}", else_expr); } - for (const auto& ssbo : ctx.info.storage_buffers_descriptors) { - if (ssbo.is_global_fallback) { - ctx.Add("ENDIF;"); - } + const size_t num_used_buffers{ctx.info.nvn_buffer_used.count()}; + for (size_t index = 0; index < num_used_buffers; ++index) { + ctx.Add("ENDIF;"); } } diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp index 66ecfc9f74..8ec180bc42 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -28,11 +31,6 @@ struct FuncTraits { using ArgType = std::tuple_element_t>; }; -template -void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) { - inst->SetDefinition(func(ctx, std::forward(args)...)); -} - template auto Arg(EmitContext& ctx, const IR::Value& arg) { if constexpr (std::is_same_v) { @@ -53,21 +51,10 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) { template void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence) { using Traits = FuncTraits; - if constexpr (std::is_same_v) { - if constexpr (is_first_arg_inst) { - SetDefinition( - ctx, inst, *inst, - Arg>(ctx, inst->Arg(I))...); - } else { - SetDefinition( - ctx, inst, Arg>(ctx, inst->Arg(I))...); - } + if constexpr (is_first_arg_inst) { + func(ctx, *inst, Arg>(ctx, inst->Arg(I))...); } else { - if constexpr (is_first_arg_inst) { - func(ctx, *inst, Arg>(ctx, inst->Arg(I))...); - } else { - func(ctx, Arg>(ctx, inst->Arg(I))...); - } + func(ctx, Arg>(ctx, inst->Arg(I))...); } } diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 4f4d420bd7..a2df6159fb 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -633,7 +633,7 @@ std::string EmitContext::DefineGlobalMemoryFunctions() { std::string load_func_128{"uvec4 LoadGlobal128(uint64_t addr){"}; const size_t num_buffers{info.storage_buffers_descriptors.size()}; for (size_t index = 0; index < num_buffers; ++index) { - if (!info.storage_buffers_descriptors[index].is_global_fallback) { + if (!info.nvn_buffer_used[index]) { continue; } define_body(write_func, index, "{0}[uint(addr-{1})>>2]=data;return;}}"); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 465c40277a..d1c08c1131 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -417,48 +417,48 @@ Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const I return ctx.OpPackHalf2x16(ctx.U32[1], result); } -Id EmitGlobalAtomicIAdd32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicIAdd32, ctx.U32[1], address, value); +Id EmitGlobalAtomicIAdd32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicSMin32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicSMin32, ctx.U32[1], address, value); +Id EmitGlobalAtomicSMin32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicUMin32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicUMin32, ctx.U32[1], address, value); +Id EmitGlobalAtomicUMin32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicSMax32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicSMax32, ctx.U32[1], address, value); +Id EmitGlobalAtomicSMax32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicUMax32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicUMax32, ctx.U32[1], address, value); +Id EmitGlobalAtomicUMax32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicInc32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicInc32, ctx.U32[1], address, value); +Id EmitGlobalAtomicInc32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicDec32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicDec32, ctx.U32[1], address, value); +Id EmitGlobalAtomicDec32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicAnd32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicAnd32, ctx.U32[1], address, value); +Id EmitGlobalAtomicAnd32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicOr32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicOr32, ctx.U32[1], address, value); +Id EmitGlobalAtomicOr32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicXor32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicXor32, ctx.U32[1], address, value); +Id EmitGlobalAtomicXor32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicExchange32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicExchange32, ctx.U32[1], address, value); +Id EmitGlobalAtomicExchange32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } Id EmitGlobalAtomicIAdd64(EmitContext&) { @@ -549,32 +549,32 @@ Id EmitGlobalAtomicExchange32x2(EmitContext&) { throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicAddF32(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicAddF32, ctx.F32[1], address, value); +Id EmitGlobalAtomicAddF32(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicAddF16x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicAddF16x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicAddF16x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicAddF32x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicAddF32x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicAddF32x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicMinF16x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicMinF16x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicMinF16x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicMinF32x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicMinF32x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicMinF32x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicMaxF16x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicMaxF16x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx, Id address, Id value) { - return ctx.CallGlobalMemory(IR::Opcode::GlobalAtomicMaxF32x2, ctx.U32[1], address, value); +Id EmitGlobalAtomicMaxF32x2(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 9d49289dd7..165ec3fe84 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -90,17 +90,17 @@ Id EmitUndefU8(EmitContext& ctx); Id EmitUndefU16(EmitContext& ctx); Id EmitUndefU32(EmitContext& ctx); Id EmitUndefU64(EmitContext& ctx); -Id EmitLoadGlobalU8(EmitContext& ctx, Id address); -Id EmitLoadGlobalS8(EmitContext& ctx, Id address); -Id EmitLoadGlobalU16(EmitContext& ctx, Id address); -Id EmitLoadGlobalS16(EmitContext& ctx, Id address); +void EmitLoadGlobalU8(EmitContext& ctx); +void EmitLoadGlobalS8(EmitContext& ctx); +void EmitLoadGlobalU16(EmitContext& ctx); +void EmitLoadGlobalS16(EmitContext& ctx); Id EmitLoadGlobal32(EmitContext& ctx, Id address); Id EmitLoadGlobal64(EmitContext& ctx, Id address); Id EmitLoadGlobal128(EmitContext& ctx, Id address); -void EmitWriteGlobalU8(EmitContext& ctx, Id address, Id value); -void EmitWriteGlobalS8(EmitContext& ctx, Id address, Id value); -void EmitWriteGlobalU16(EmitContext& ctx, Id address, Id value); -void EmitWriteGlobalS16(EmitContext& ctx, Id address, Id value); +void EmitWriteGlobalU8(EmitContext& ctx); +void EmitWriteGlobalS8(EmitContext& ctx); +void EmitWriteGlobalU16(EmitContext& ctx); +void EmitWriteGlobalS16(EmitContext& ctx); void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value); void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value); void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value); @@ -415,17 +415,17 @@ Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const I Id value); Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value); -Id EmitGlobalAtomicIAdd32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicSMin32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicUMin32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicSMax32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicUMax32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicInc32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicDec32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicAnd32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicOr32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicXor32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicExchange32(EmitContext& ctx, Id address, Id value); +Id EmitGlobalAtomicIAdd32(EmitContext& ctx); +Id EmitGlobalAtomicSMin32(EmitContext& ctx); +Id EmitGlobalAtomicUMin32(EmitContext& ctx); +Id EmitGlobalAtomicSMax32(EmitContext& ctx); +Id EmitGlobalAtomicUMax32(EmitContext& ctx); +Id EmitGlobalAtomicInc32(EmitContext& ctx); +Id EmitGlobalAtomicDec32(EmitContext& ctx); +Id EmitGlobalAtomicAnd32(EmitContext& ctx); +Id EmitGlobalAtomicOr32(EmitContext& ctx); +Id EmitGlobalAtomicXor32(EmitContext& ctx); +Id EmitGlobalAtomicExchange32(EmitContext& ctx); Id EmitGlobalAtomicIAdd64(EmitContext& ctx); Id EmitGlobalAtomicSMin64(EmitContext& ctx); Id EmitGlobalAtomicUMin64(EmitContext& ctx); @@ -448,13 +448,13 @@ Id EmitGlobalAtomicAnd32x2(EmitContext& ctx); Id EmitGlobalAtomicOr32x2(EmitContext& ctx); Id EmitGlobalAtomicXor32x2(EmitContext& ctx); Id EmitGlobalAtomicExchange32x2(EmitContext& ctx); -Id EmitGlobalAtomicAddF32(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicAddF16x2(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicAddF32x2(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicMinF16x2(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicMinF32x2(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx, Id address, Id value); -Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx, Id address, Id value); +Id EmitGlobalAtomicAddF32(EmitContext& ctx); +Id EmitGlobalAtomicAddF16x2(EmitContext& ctx); +Id EmitGlobalAtomicAddF32x2(EmitContext& ctx); +Id EmitGlobalAtomicMinF16x2(EmitContext& ctx); +Id EmitGlobalAtomicMinF32x2(EmitContext& ctx); +Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx); +Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx); Id EmitLogicalOr(EmitContext& ctx, Id a, Id b); Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b); Id EmitLogicalXor(EmitContext& ctx, Id a, Id b); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index cc72f4baf1..bef4421fed 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp @@ -69,71 +69,93 @@ void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& &StorageDefinitions::U32, index_offset); } -void WriteStorageBits(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, - Id value, Id bit_offset, Id bit_count) { +void WriteStorageByCasLoop(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, + Id value, Id bit_offset, Id bit_count) { const Id pointer{StoragePointer(ctx, binding, offset, ctx.storage_types.U32, sizeof(u32), &StorageDefinitions::U32)}; - ctx.AtomicBitFieldInsert(pointer, value, bit_offset, bit_count); + ctx.OpFunctionCall(ctx.TypeVoid(), ctx.write_storage_cas_loop_func, pointer, value, bit_offset, + bit_count); } } // Anonymous namespace -Id EmitLoadGlobalU8(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobalU8, ctx.U32[1], address, ctx.u32_zero_value); +void EmitLoadGlobalU8(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitLoadGlobalS8(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobalS8, ctx.U32[1], address, ctx.u32_zero_value); +void EmitLoadGlobalS8(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitLoadGlobalU16(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobalU16, ctx.U32[1], address, - ctx.u32_zero_value); +void EmitLoadGlobalU16(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -Id EmitLoadGlobalS16(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobalS16, ctx.U32[1], address, - ctx.u32_zero_value); +void EmitLoadGlobalS16(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } Id EmitLoadGlobal32(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobal32, ctx.U32[1], address, ctx.u32_zero_value); + if (ctx.profile.support_int64) { + return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address); + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); + return ctx.Const(0u); } Id EmitLoadGlobal64(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobal64, ctx.U32[2], address, ctx.u32_zero_value); + if (ctx.profile.support_int64) { + return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address); + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); + return ctx.Const(0u, 0u); } Id EmitLoadGlobal128(EmitContext& ctx, Id address) { - return ctx.CallGlobalMemory(IR::Opcode::LoadGlobal128, ctx.U32[4], address, - ctx.u32_zero_value); + if (ctx.profile.support_int64) { + return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address); + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); + return ctx.Const(0u, 0u, 0u, 0u); } -void EmitWriteGlobalU8(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobalU8, ctx.void_id, address, value); +void EmitWriteGlobalU8(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -void EmitWriteGlobalS8(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobalS8, ctx.void_id, address, value); +void EmitWriteGlobalS8(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -void EmitWriteGlobalU16(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobalU16, ctx.void_id, address, value); +void EmitWriteGlobalU16(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } -void EmitWriteGlobalS16(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobalS16, ctx.void_id, address, value); +void EmitWriteGlobalS16(EmitContext&) { + throw NotImplementedException("SPIR-V Instruction"); } void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobal32, ctx.void_id, address, value); + if (ctx.profile.support_int64) { + ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value); + return; + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); } void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobal64, ctx.void_id, address, value); + if (ctx.profile.support_int64) { + ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value); + return; + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); } void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) { - ctx.CallGlobalMemory(IR::Opcode::WriteGlobal128, ctx.void_id, address, value); + if (ctx.profile.support_int64) { + ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value); + return; + } + LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); } Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { @@ -217,7 +239,7 @@ void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Va WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, sizeof(u8), &StorageDefinitions::U8); } else { - WriteStorageBits(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); } } @@ -228,7 +250,7 @@ void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Va WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, sizeof(s8), &StorageDefinitions::S8); } else { - WriteStorageBits(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u)); } } @@ -239,7 +261,7 @@ void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::V WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, sizeof(u16), &StorageDefinitions::U16); } else { - WriteStorageBits(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); } } @@ -250,7 +272,7 @@ void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::V WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, sizeof(s16), &StorageDefinitions::S16); } else { - WriteStorageBits(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); + WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u)); } } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index f174251625..b562e82983 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -486,7 +486,8 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf DefineTextures(program.info, texture_binding, bindings.texture_scaling_index); DefineImages(program.info, image_binding, bindings.image_scaling_index); DefineAttributeMemAccess(program.info); - DefineGlobalMemoryFunctions(program); + DefineWriteStorageCasLoopFunction(program.info); + DefineGlobalMemoryFunctions(program.info); DefineRescalingInput(program.info); DefineRenderArea(program.info); } @@ -531,24 +532,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) { return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u)); } -Id EmitContext::CallGlobalMemory(IR::Opcode opcode, Id result_type, Id address, Id value) { - if (profile.support_int64) { - return OpFunctionCall(result_type, global_memory_funcs.at(opcode), address, value); - } - if (result_type.value == void_id.value) { - return Id{}; - } - return ConstantNull(result_type); -} - -void EmitContext::AtomicBitFieldInsert(Id pointer, Id value, Id offset, Id count) { - const Id scope{Const(static_cast(spv::Scope::Device))}; - const Id mask{OpBitFieldInsert(U32[1], u32_zero_value, Const(0xFFFFFFFFU), offset, count)}; - const Id bits{OpBitFieldInsert(U32[1], u32_zero_value, value, offset, count)}; - OpAtomicAnd(U32[1], pointer, scope, u32_zero_value, OpNot(U32[1], mask)); - OpAtomicOr(U32[1], pointer, scope, u32_zero_value, bits); -} - void EmitContext::DefineCommonTypes(const Info& info) { void_id = TypeVoid(); @@ -906,256 +889,135 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { } } -void EmitContext::DefineGlobalMemoryFunctions(const IR::Program& program) { - const Info& info{program.info}; +void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) { + if (profile.support_int8 && profile.support_int16) { + return; + } + if (!info.uses_int8 && !info.uses_int16) { + return; + } + + AddCapability(spv::Capability::VariablePointersStorageBuffer); + + const Id ptr_type{TypePointer(spv::StorageClass::StorageBuffer, U32[1])}; + const Id func_type{TypeFunction(void_id, ptr_type, U32[1], U32[1], U32[1])}; + const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)}; + const Id pointer{OpFunctionParameter(ptr_type)}; + const Id value{OpFunctionParameter(U32[1])}; + const Id bit_offset{OpFunctionParameter(U32[1])}; + const Id bit_count{OpFunctionParameter(U32[1])}; + + AddLabel(); + const Id scope_device{Const(1u)}; + const Id ordering_relaxed{u32_zero_value}; + const Id body_label{OpLabel()}; + const Id continue_label{OpLabel()}; + const Id endloop_label{OpLabel()}; + const Id beginloop_label{OpLabel()}; + OpBranch(beginloop_label); + + AddLabel(beginloop_label); + OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone); + OpBranch(body_label); + + AddLabel(body_label); + const Id expected_value{OpLoad(U32[1], pointer)}; + const Id desired_value{OpBitFieldInsert(U32[1], expected_value, value, bit_offset, bit_count)}; + const Id actual_value{OpAtomicCompareExchange(U32[1], pointer, scope_device, ordering_relaxed, + ordering_relaxed, desired_value, expected_value)}; + const Id store_successful{OpIEqual(U1, expected_value, actual_value)}; + OpBranchConditional(store_successful, endloop_label, continue_label); + + AddLabel(endloop_label); + OpReturn(); + + AddLabel(continue_label); + OpBranch(beginloop_label); + + OpFunctionEnd(); + + write_storage_cas_loop_func = func; +} + +void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { if (!info.uses_global_memory || !profile.support_int64) { return; } + using DefPtr = Id StorageDefinitions::*; const Id zero{u32_zero_value}; - const Id scope{Const(static_cast(spv::Scope::Device))}; - const Id align_mask{Const(~(static_cast(profile.min_ssbo_alignment) - 1U))}; - const auto word_pointer{[&](Id ssbo, Id word, u32 element) { - return OpAccessChain(storage_types.U32.element, ssbo, zero, - OpIAdd(U32[1], word, Const(element))); - }}; - const auto cbuf_word{[&](u32 index, u32 offset) { - if (profile.support_descriptor_aliasing) { - return OpLoad(U32[1], OpAccessChain(uniform_types.U32, cbufs[index].U32, zero, - Const(offset / 4))); - } - const Id vector{OpLoad(U32[4], OpAccessChain(uniform_types.U32x4, cbufs[index].U32x4, - zero, Const(offset / 16)))}; - return OpCompositeExtract(U32[1], vector, (offset / 4) % 4); - }}; - const auto bits{[&](Id offset, u32 count) { - return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], offset, Const(3U)), - Const(32U - count)); - }}; - const auto define{[&](IR::Opcode opcode, Id result_type, Id value_type, auto&& callback) { - const std::array params{U64, value_type}; - const Id func{OpFunction(result_type, spv::FunctionControlMask::MaskNone, - TypeFunction(result_type, params))}; - const Id addr{OpFunctionParameter(U64)}; - const Id value{OpFunctionParameter(value_type)}; - const bool returns_value{result_type.value != void_id.value}; + const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift, + auto&& callback) { AddLabel(); - const Id addr_words{OpBitcast(U32[2], addr)}; - const Id addr_low{OpCompositeExtract(U32[1], addr_words, 0U)}; - const Id addr_high{OpCompositeExtract(U32[1], addr_words, 1U)}; - for (size_t index = 0; index < info.storage_buffers_descriptors.size(); ++index) { - const auto& desc{info.storage_buffers_descriptors[index]}; - if (!desc.is_global_fallback) { + const size_t num_buffers{info.storage_buffers_descriptors.size()}; + for (size_t index = 0; index < num_buffers; ++index) { + if (!info.nvn_buffer_used[index]) { continue; } - const Id ssbo_low{ - OpBitwiseAnd(U32[1], cbuf_word(desc.cbuf_index, desc.cbuf_offset), align_mask)}; - const Id ssbo_high{cbuf_word(desc.cbuf_index, desc.cbuf_offset + 4)}; - const Id ssbo_size{cbuf_word(desc.cbuf_index, desc.cbuf_offset + 8)}; - const Id offset{OpISub(U32[1], addr_low, ssbo_low)}; - const Id borrow{ - OpSelect(U32[1], OpULessThan(U1, addr_low, ssbo_low), Const(1U), zero)}; - const Id cond{OpLogicalAnd(U1, OpULessThan(U1, offset, ssbo_size), - OpIEqual(U1, OpISub(U32[1], addr_high, borrow), ssbo_high))}; + const auto& ssbo{info.storage_buffers_descriptors[index]}; + const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)}; + const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)}; + const Id ssbo_addr_pointer{OpAccessChain( + uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero, ssbo_addr_cbuf_offset)}; + const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, + zero, ssbo_size_cbuf_offset)}; + + const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; + const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; + const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; + const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; + const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; + const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), + OpULessThan(U1, addr, ssbo_end))}; const Id then_label{OpLabel()}; const Id else_label{OpLabel()}; OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone); OpBranchConditional(cond, then_label, else_label); AddLabel(then_label); - const Id word{OpShiftRightLogical(U32[1], offset, Const(2U))}; - const Id result{callback(ssbos[index], word, offset, value)}; - if (returns_value) { - OpReturnValue(result); - } else { - OpReturn(); - } + const Id ssbo_id{ssbos[index].*ssbo_member}; + const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))}; + const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))}; + const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)}; + callback(ssbo_pointer); AddLabel(else_label); } - if (returns_value) { - OpReturnValue(ConstantNull(result_type)); - } else { - OpReturn(); - } + }}; + const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { + const Id function_type{TypeFunction(type, U64)}; + const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)}; + const Id addr{OpFunctionParameter(U64)}; + define_body(ssbo_member, addr, element_pointer, shift, + [&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); }); + OpReturnValue(ConstantNull(type)); OpFunctionEnd(); - global_memory_funcs.emplace(opcode, func); + return func_id; }}; - const auto vector_pointer{[&](const StorageDefinitions& ssbo, Id word, u32 count) { - if (count == 2) { - return OpAccessChain(storage_types.U32x2.element, ssbo.U32x2, zero, - OpShiftRightLogical(U32[1], word, Const(1U))); - } - return OpAccessChain(storage_types.U32x4.element, ssbo.U32x4, zero, - OpShiftRightLogical(U32[1], word, Const(2U))); + const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { + const Id function_type{TypeFunction(void_id, U64, type)}; + const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)}; + const Id addr{OpFunctionParameter(U64)}; + const Id data{OpFunctionParameter(type)}; + define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) { + OpStore(ssbo_pointer, data); + OpReturn(); + }); + OpReturn(); + OpFunctionEnd(); + return func_id; }}; - const auto load{[&](Id type, u32 count) { - return [&, type, count](const StorageDefinitions& ssbo, Id word, Id, Id) { - if (count > 1 && profile.support_descriptor_aliasing) { - return OpLoad(type, vector_pointer(ssbo, word, count)); - } - std::array words{}; - for (u32 element = 0; element < count; ++element) { - words[element] = OpLoad(U32[1], word_pointer(ssbo.U32, word, element)); - } - if (count == 1) { - return words[0]; - } - return OpCompositeConstruct(type, std::span(words.data(), count)); - }; - }}; - const auto store{[&](u32 count) { - return [&, count](const StorageDefinitions& ssbo, Id word, Id, Id value) { - if (count > 1 && profile.support_descriptor_aliasing) { - OpStore(vector_pointer(ssbo, word, count), value); - return Id{}; - } - if (count == 1) { - OpStore(word_pointer(ssbo.U32, word, 0), value); - return Id{}; - } - for (u32 element = 0; element < count; ++element) { - OpStore(word_pointer(ssbo.U32, word, element), - OpCompositeExtract(U32[1], value, element)); - } - return Id{}; - }; - }}; - const auto extract{[&](bool is_signed, u32 count) { - return [&, is_signed, count](const StorageDefinitions& ssbo, Id word, Id offset, Id) { - const Id loaded{OpLoad(U32[1], word_pointer(ssbo.U32, word, 0))}; - if (is_signed) { - return OpBitFieldSExtract(U32[1], loaded, bits(offset, count), Const(count)); - } - return OpBitFieldUExtract(U32[1], loaded, bits(offset, count), Const(count)); - }; - }}; - const auto insert{[&](u32 count) { - return [&, count](const StorageDefinitions& ssbo, Id word, Id offset, Id value) { - AtomicBitFieldInsert(word_pointer(ssbo.U32, word, 0), value, bits(offset, count), - Const(count)); - return Id{}; - }; - }}; - const auto atomic{[&](Id (Sirit::Module::*func)(Id, Id, Id, Id, Id)) { - return [&, func](const StorageDefinitions& ssbo, Id word, Id, Id value) { - return (this->*func)(U32[1], word_pointer(ssbo.U32, word, 0), scope, zero, value); - }; - }}; - const auto cas{[&](Id type, Id helper) { - return [&, type, helper](const StorageDefinitions& ssbo, Id word, Id, Id value) { - return OpFunctionCall(type, helper, word, value, ssbo.U32); - }; - }}; - const auto packed{[&](bool is_half, Id helper) { - return [&, is_half, helper](const StorageDefinitions& ssbo, Id word, Id, Id value) { - if (is_half) { - return OpBitcast(U32[1], OpFunctionCall(F16[2], helper, word, value, ssbo.U32)); - } - return OpPackHalf2x16(U32[1], OpFunctionCall(F32[2], helper, word, value, ssbo.U32)); - }; - }}; - for (const IR::Block* const block : program.post_order_blocks) { - for (const IR::Inst& inst : block->Instructions()) { - const IR::Opcode opcode{inst.GetOpcode()}; - if (global_memory_funcs.contains(opcode)) { - continue; - } - switch (opcode) { - case IR::Opcode::LoadGlobalU8: - define(opcode, U32[1], U32[1], extract(false, 8)); - break; - case IR::Opcode::LoadGlobalS8: - define(opcode, U32[1], U32[1], extract(true, 8)); - break; - case IR::Opcode::LoadGlobalU16: - define(opcode, U32[1], U32[1], extract(false, 16)); - break; - case IR::Opcode::LoadGlobalS16: - define(opcode, U32[1], U32[1], extract(true, 16)); - break; - case IR::Opcode::LoadGlobal32: - define(opcode, U32[1], U32[1], load(U32[1], 1)); - break; - case IR::Opcode::LoadGlobal64: - define(opcode, U32[2], U32[1], load(U32[2], 2)); - break; - case IR::Opcode::LoadGlobal128: - define(opcode, U32[4], U32[1], load(U32[4], 4)); - break; - case IR::Opcode::WriteGlobalU8: - case IR::Opcode::WriteGlobalS8: - define(opcode, void_id, U32[1], insert(8)); - break; - case IR::Opcode::WriteGlobalU16: - case IR::Opcode::WriteGlobalS16: - define(opcode, void_id, U32[1], insert(16)); - break; - case IR::Opcode::WriteGlobal32: - define(opcode, void_id, U32[1], store(1)); - break; - case IR::Opcode::WriteGlobal64: - define(opcode, void_id, U32[2], store(2)); - break; - case IR::Opcode::WriteGlobal128: - define(opcode, void_id, U32[4], store(4)); - break; - case IR::Opcode::GlobalAtomicIAdd32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicIAdd)); - break; - case IR::Opcode::GlobalAtomicSMin32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicSMin)); - break; - case IR::Opcode::GlobalAtomicUMin32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicUMin)); - break; - case IR::Opcode::GlobalAtomicSMax32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicSMax)); - break; - case IR::Opcode::GlobalAtomicUMax32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicUMax)); - break; - case IR::Opcode::GlobalAtomicAnd32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicAnd)); - break; - case IR::Opcode::GlobalAtomicOr32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicOr)); - break; - case IR::Opcode::GlobalAtomicXor32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicXor)); - break; - case IR::Opcode::GlobalAtomicExchange32: - define(opcode, U32[1], U32[1], atomic(&Sirit::Module::OpAtomicExchange)); - break; - case IR::Opcode::GlobalAtomicInc32: - define(opcode, U32[1], U32[1], cas(U32[1], increment_cas_ssbo)); - break; - case IR::Opcode::GlobalAtomicDec32: - define(opcode, U32[1], U32[1], cas(U32[1], decrement_cas_ssbo)); - break; - case IR::Opcode::GlobalAtomicAddF32: - define(opcode, F32[1], F32[1], cas(F32[1], f32_add_cas)); - break; - case IR::Opcode::GlobalAtomicAddF16x2: - define(opcode, U32[1], F16[2], packed(true, f16x2_add_cas)); - break; - case IR::Opcode::GlobalAtomicMinF16x2: - define(opcode, U32[1], F16[2], packed(true, f16x2_min_cas)); - break; - case IR::Opcode::GlobalAtomicMaxF16x2: - define(opcode, U32[1], F16[2], packed(true, f16x2_max_cas)); - break; - case IR::Opcode::GlobalAtomicAddF32x2: - define(opcode, U32[1], F32[2], packed(false, f32x2_add_cas)); - break; - case IR::Opcode::GlobalAtomicMinF32x2: - define(opcode, U32[1], F32[2], packed(false, f32x2_min_cas)); - break; - case IR::Opcode::GlobalAtomicMaxF32x2: - define(opcode, U32[1], F32[2], packed(false, f32x2_max_cas)); - break; - default: - break; - } - } - } + const auto define{ + [&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) { + const Id element_type{type_def.element}; + const u32 shift{static_cast(std::countr_zero(size))}; + const Id load_func{define_load(ssbo_member, element_type, type, shift)}; + const Id write_func{define_write(ssbo_member, element_type, type, shift)}; + return std::make_pair(load_func, write_func); + }}; + std::tie(load_global_func_u32, write_global_func_u32) = + define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32)); + std::tie(load_global_func_u32x2, write_global_func_u32x2) = + define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2])); + std::tie(load_global_func_u32x4, write_global_func_u32x4) = + define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4])); } void EmitContext::DefineRescalingInput(const Info& info) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 68761e1f1a..a5821e9e93 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -7,7 +7,6 @@ #pragma once #include -#include #include #include "common/container/unordered_set.h" @@ -174,9 +173,6 @@ public: [[nodiscard]] Id BitOffset8(const IR::Value& offset); [[nodiscard]] Id BitOffset16(const IR::Value& offset); - Id CallGlobalMemory(IR::Opcode opcode, Id result_type, Id address, Id value); - void AtomicBitFieldInsert(Id pointer, Id value, Id offset, Id count); - Id Const(u32 value) { return Constant(U32[1], value); } @@ -339,7 +335,14 @@ public: Id f32x2_min_cas{}; Id f32x2_max_cas{}; - std::unordered_map global_memory_funcs; + Id write_storage_cas_loop_func{}; + + Id load_global_func_u32{}; + Id load_global_func_u32x2{}; + Id load_global_func_u32x4{}; + Id write_global_func_u32{}; + Id write_global_func_u32x2{}; + Id write_global_func_u32x4{}; bool need_input_position_indirect{}; Id input_position{}; @@ -390,7 +393,8 @@ private: void DefineTextures(const Info& info, u32& binding, u32& scaling_index); void DefineImages(const Info& info, u32& binding, u32& scaling_index); void DefineAttributeMemAccess(const Info& info); - void DefineGlobalMemoryFunctions(const IR::Program& program); + void DefineWriteStorageCasLoopFunction(const Info& info); + void DefineGlobalMemoryFunctions(const Info& info); void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); void DefineRescalingInputUniformConstant(); diff --git a/src/shader_recompiler/frontend/ir/breadth_first_search.h b/src/shader_recompiler/frontend/ir/breadth_first_search.h index ec55425549..afe153f180 100644 --- a/src/shader_recompiler/frontend/ir/breadth_first_search.h +++ b/src/shader_recompiler/frontend/ir/breadth_first_search.h @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include #include #include @@ -13,9 +17,10 @@ namespace Shader::IR { -template -auto BreadthFirstSearch(const Value& value, Pred&& pred) +template +auto BreadthFirstSearch(Value value, Pred&& pred, Resolve&& resolve = {}) -> std::invoke_result_t { + value = resolve(value); if (value.IsImmediate()) { // Nothing to do with immediates return std::nullopt; @@ -36,7 +41,7 @@ auto BreadthFirstSearch(const Value& value, Pred&& pred) } // Visit the right most arguments first for (size_t arg = inst->NumArgs(); arg--;) { - const Value arg_value{inst->Arg(arg)}; + const Value arg_value{resolve(inst->Arg(arg))}; if (arg_value.IsImmediate()) { continue; } diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp index 9497c33e30..75087777dd 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -93,7 +93,7 @@ IR::U64 AtomOffset(TranslatorVisitor& v, u64 insn) { } const mem{insn}; const IR::U64 address{[&]() -> IR::U64 { - if (mem.e == 0 || mem.addr_reg == IR::Reg::RZ) + if (mem.e == 0) return v.ir.UConvert(64, v.X(mem.addr_reg)); return v.L(mem.addr_reg); }()}; @@ -108,9 +108,14 @@ IR::U64 AtomOffset(TranslatorVisitor& v, u64 insn) { return v.ir.IAdd(address, v.ir.Imm64(addr_offset)); } +// INC, DEC for U32/S32/U64 does nothing +// ADD, INC, DEC for S64 does nothing +// Only ADD does something for F32 +// Only ADD, MIN and MAX does something for F16x2 bool AtomOpNotApplicable(AtomSize size, AtomOp op) { // TODO: SAFEADD switch (size) { + case AtomSize::U32: case AtomSize::S32: case AtomSize::U64: return (op == AtomOp::INC || op == AtomOp::DEC); diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp index c210f634b0..aa494e24cc 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/barrier_operations.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -36,12 +36,6 @@ void TranslatorVisitor::DEPBAR(u64) { // DEPBAR is a no-op } -void TranslatorVisitor::CCTL(u64) {} - -void TranslatorVisitor::CCTLL(u64) {} - -void TranslatorVisitor::CCTLT(u64) {} - void TranslatorVisitor::BAR(u64 insn) { enum class Mode { RedPopc, diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp index d700a05d59..8f2cf897d4 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -58,7 +55,7 @@ IR::U64 Address(TranslatorVisitor& v, u64 insn) { } const mem{insn}; const IR::U64 address{[&]() -> IR::U64 { - if (mem.e == 0 || mem.addr_reg == IR::Reg::RZ) { + if (mem.e == 0) { // LDG/STG without .E uses a 32-bit pointer, zero-extend it return v.ir.UConvert(64, v.X(mem.addr_reg)); } diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp index 5e12c37430..369af8e03a 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -43,6 +43,18 @@ void TranslatorVisitor::CAL(u64) { // CAL is a no-op } +void TranslatorVisitor::CCTL(u64) { + ThrowNotImplemented(Opcode::CCTL); +} + +void TranslatorVisitor::CCTLL(u64) { + ThrowNotImplemented(Opcode::CCTLL); +} + +void TranslatorVisitor::CCTLT(u64) { + ThrowNotImplemented(Opcode::CCTLT); +} + void TranslatorVisitor::CONT(u64) { ThrowNotImplemented(Opcode::CONT); } diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index d1d45d6b33..0cc4b3b844 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -121,12 +121,9 @@ void AddNVNStorageBuffers(IR::Program& program) { continue; } const u32 offset{base + index * descriptor_size}; - const auto it{std::ranges::find_if(descs, [&](const StorageBufferDescriptor& desc) { - return desc.cbuf_index == driver_cbuf && desc.cbuf_offset == offset; - })}; + const auto it{std::ranges::find(descs, offset, &StorageBufferDescriptor::cbuf_offset)}; if (it != descs.end()) { it->is_written |= program.info.stores_global_memory; - it->is_global_fallback = true; continue; } descs.push_back({ @@ -134,7 +131,6 @@ void AddNVNStorageBuffers(IR::Program& program) { .cbuf_offset = offset, .count = 1, .is_written = program.info.stores_global_memory, - .is_global_fallback = true, }); } } @@ -400,7 +396,7 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool #include -#include #include #include #include "common/alignment.h" #include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/breadth_first_search.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/value.h" #include "shader_recompiler/host_translate_info.h" @@ -50,7 +49,6 @@ using StorageBufferSet = using StorageInstVector = small_vector; using StorageWritesSet = flat_set, small_vector>; -using LocalStores = std::multimap; struct StorageInfo { StorageBufferSet set; @@ -335,7 +333,7 @@ std::optional TrackLowAddress(IR::Inst* inst) { } /// Tries to track the storage buffer address used by a global memory instruction -StorageBufferSet Track(const IR::Value& value, const Bias* bias, const LocalStores& local_stores) { +std::optional Track(const IR::Value& value, const Bias* bias) { const auto pred{[bias](const IR::Inst* inst) -> std::optional { if (inst->GetOpcode() != IR::Opcode::GetCbufU32 && inst->GetOpcode() != IR::Opcode::GetCbufU32x2) { @@ -368,83 +366,11 @@ StorageBufferSet Track(const IR::Value& value, const Bias* bias, const LocalStor } return storage_buffer; }}; - StorageBufferSet result; - std::unordered_set visited; - small_vector pending; - const auto push{[&](const IR::Value& arg) { - if (!arg.IsImmediate() && visited.insert(arg.InstRecursive()).second) { - pending.push_back(arg.InstRecursive()); - } - }}; - push(value); - while (!pending.empty()) { - const IR::Inst* const inst{pending.back()}; - pending.pop_back(); - if (const std::optional storage_buffer{pred(inst)}) { - result.insert(*storage_buffer); - continue; - } - switch (inst->GetOpcode()) { - case IR::Opcode::LoadLocal: - if (inst->Arg(0).IsImmediate()) { - const auto [begin, end]{local_stores.equal_range(inst->Arg(0).U32())}; - for (auto it = begin; it != end; ++it) { - push(it->second); - } - } - continue; - case IR::Opcode::SelectU32: - case IR::Opcode::SelectU64: - push(inst->Arg(1)); - push(inst->Arg(2)); - continue; - case IR::Opcode::GetCbufU8: - case IR::Opcode::GetCbufS8: - case IR::Opcode::GetCbufU16: - case IR::Opcode::GetCbufS16: - case IR::Opcode::GetCbufU32: - case IR::Opcode::GetCbufF32: - case IR::Opcode::GetCbufU32x2: - case IR::Opcode::LoadSharedU8: - case IR::Opcode::LoadSharedS8: - case IR::Opcode::LoadSharedU16: - case IR::Opcode::LoadSharedS16: - case IR::Opcode::LoadSharedU32: - case IR::Opcode::LoadSharedU64: - case IR::Opcode::LoadSharedU128: - continue; - default: - break; - } - if (IsGlobalMemory(*inst) || inst->MayHaveSideEffects()) { - continue; - } - for (size_t arg = 0; arg < inst->NumArgs(); ++arg) { - push(inst->Arg(arg)); - } - } - return result; -} - -LocalStores GatherLocalStores(const IR::Program& program) { - LocalStores stores; - for (IR::Block* const block : program.post_order_blocks) { - for (const IR::Inst& inst : block->Instructions()) { - if (inst.GetOpcode() != IR::Opcode::WriteLocal) { - continue; - } - if (!inst.Arg(0).IsImmediate()) { - return {}; - } - stores.emplace(inst.Arg(0).U32(), inst.Arg(1)); - } - } - return stores; + return BreadthFirstSearch(value, pred); } /// Collects the storage buffer used by a global memory instruction and the instruction itself -void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info, - const LocalStores& local_stores) { +void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) { // NVN puts storage buffers in a specific range, we have to bias towards these addresses to // avoid getting false positives static constexpr Bias nvn_bias{ @@ -461,24 +387,25 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info, } // First try to find storage buffers in the NVN address const IR::U32 low_addr{low_addr_info->value}; - StorageBufferSet candidates{Track(low_addr, &nvn_bias, local_stores)}; - if (candidates.empty()) { + std::optional storage_buffer{Track(low_addr, &nvn_bias)}; + if (!storage_buffer) { // If it fails, track without a bias - candidates = Track(low_addr, nullptr, local_stores); + storage_buffer = Track(low_addr, nullptr); + if (!storage_buffer) { + // If that also fails, use NVN fallbacks + LOG_WARNING(Shader, "Storage buffer failed to track, using global memory fallbacks"); + return; + } + LOG_WARNING(Shader, "Storage buffer tracked without bias, index {} offset {}", + storage_buffer->index, storage_buffer->offset); } - if (candidates.size() != 1) { - // If that also fails, use NVN fallbacks - LOG_WARNING(Shader, "Storage buffer failed to track, using global memory fallbacks"); - return; - } - const StorageBufferAddr storage_buffer{*candidates.begin()}; // Collect storage buffer and the instruction if (IsGlobalMemoryWrite(inst)) { - info.writes.insert(storage_buffer); + info.writes.insert(*storage_buffer); } - info.set.insert(storage_buffer); + info.set.insert(*storage_buffer); info.to_replace.push_back(StorageInst{ - .storage_buffer{storage_buffer}, + .storage_buffer{*storage_buffer}, .inst = &inst, .block = &block, }); @@ -598,13 +525,12 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) { StorageInfo info; - const LocalStores local_stores{GatherLocalStores(program)}; for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { if (!IsGlobalMemory(inst)) { continue; } - CollectStorageBuffers(*block, inst, info, local_stores); + CollectStorageBuffers(*block, inst, info); } } for (const StorageBufferAddr& storage_buffer : info.set) { @@ -636,7 +562,6 @@ void JoinStorageInfo(Info& base, Info& source) { })}; if (it != descriptors.end()) { it->is_written |= desc.is_written; - it->is_global_fallback |= desc.is_global_fallback; continue; } descriptors.push_back(desc); diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 1ddbc58f7b..bf325a4cd9 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -32,6 +32,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); void VendorWorkaroundPass(IR::Program& program); void VerificationPass(const IR::Program& program); +bool HasBrokenPattern(const IR::Program& program); // Dual Vertex void VertexATransformPass(IR::Program& program); diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp index 1e639096a8..cae8110a26 100644 --- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp +++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp @@ -67,18 +67,11 @@ void VisitMark(IR::Block& block, IR::Inst& inst) { if (must_patch_outside) { const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{block, it}; - if (Settings::values.rescale_hack.GetValue()) { - const IR::F32 new_inst{&*block.PrependNewInst(it, inst)}; - const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())}; - const IR::Value converted{ir.FPMul(new_inst, up_factor)}; - inst.ReplaceUsesWith(converted); - } else { - IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)}; - const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})}; - const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())}; - const IR::Value converted{ir.FPMul(new_bitcast, up_factor)}; - inst.ReplaceUsesWith(converted); - } + IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)}; + const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})}; + const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())}; + const IR::Value converted{ir.FPMul(new_bitcast, up_factor)}; + inst.ReplaceUsesWith(converted); } break; } diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 0228ce117d..fae69ab91b 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -31,10 +31,63 @@ struct TextureInst { using TextureInstVector = boost::container::small_vector; +const IR::Block* ConditionalHeader(const IR::Block* body) { + const auto predecessors{body->ImmPredecessors()}; + const auto successors{body->ImmSuccessors()}; + if (predecessors.size() != 1 || successors.size() != 1) return nullptr; + const IR::Block* const header{predecessors[0]}; + const auto targets{header->ImmSuccessors()}; + if (targets.size() != 2 || targets[0] != body || targets[1] != successors[0] || + targets[1]->ImmPredecessors().size() != 2 || header->empty() || + header->back().GetOpcode() != IR::Opcode::ConditionRef) { + return nullptr; + } + return header; +} + +std::pair Condition(IR::Value value) { + bool negated{}; + while (const IR::Inst* inst = value.TryInstRecursive()) { + if (inst->GetOpcode() == IR::Opcode::LogicalNot) negated = !negated; + else if (inst->GetOpcode() != IR::Opcode::ConditionRef) break; + value = inst->Arg(0); + } + return {value.Resolve(), negated}; +} + +IR::Value ResolveConditionalPhi(IR::Value value, const IR::Block* block) { + if (!value.Resolve().IsPhi() || !(block = ConditionalHeader(block))) return value; + const auto [condition, negated]{Condition(block->back().Arg(0))}; + boost::container::small_vector visited; + while (value.Resolve().IsPhi() && std::ranges::find(visited, block) == visited.end()) { + visited.push_back(block); + const IR::Inst* const phi{value.InstRecursive()}; + const auto predecessors{block->ImmPredecessors()}; + if (predecessors.size() == 1) { + block = predecessors[0]; + continue; + } + if (predecessors.size() != 2 || phi->NumArgs() != 2) break; + const IR::Block* body{predecessors[0]}; + const IR::Block* header{ConditionalHeader(body)}; + if (header != predecessors[1]) { + body = predecessors[1]; + header = ConditionalHeader(body); + if (header != predecessors[0]) break; + } + const auto [other_condition, other_negated]{Condition(header->back().Arg(0))}; + if (condition == other_condition && (phi->PhiBlock(0) == header || phi->PhiBlock(0) == body)) { + const IR::Block* const incoming{negated == other_negated ? body : header}; + value = phi->Arg(phi->PhiBlock(0) == incoming ? 0 : 1); + } + block = header; + } + return value; +} + constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE)); -constexpr u32 DESCRIPTOR_MAX_COUNT = 128; -constexpr u32 DESCRIPTOR_CBUF_BYTES = 16 * 1024; +constexpr u32 DESCRIPTOR_MAX_COUNT = 1024; u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) { const IR::Inst* const inst = dynamic_offset.InstRecursive(); @@ -49,10 +102,11 @@ u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) { u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) { auto const descriptor_limit = (std::max)(1U, max_descriptors); - if (size_shift >= 31 || base_offset >= DESCRIPTOR_CBUF_BYTES) + auto const max_cbuf_bytes = 16 * descriptor_limit; + if (size_shift >= 31 || base_offset >= max_cbuf_bytes) return 1; auto const stride = 1U << size_shift; - auto const available = DESCRIPTOR_CBUF_BYTES - base_offset; + auto const available = max_cbuf_bytes - base_offset; if (available < DESCRIPTOR_SIZE) return 1; auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride; @@ -299,23 +353,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env, } -std::optional Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info); -static inline std::optional TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) { - if (const IR::Inst* key = v.InstRecursive()) { +std::optional Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block); +static inline std::optional TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) { + if (const IR::Inst* key = v.TryInstRecursive()) { if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second; - auto found = Track(v, env, host_info); + auto found = Track(v, env, host_info, block); if (found) env.track_cache.emplace(key, *found); return found; } - return Track(v, env, host_info); + return Track(v, env, host_info, block); } -std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info); +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block); -std::optional Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) { - return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) { - return TryGetConstBuffer(inst, env, host_info); - }); +std::optional Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) { + return IR::BreadthFirstSearch(value, [&env, &host_info, block](const IR::Inst* inst) { + return TryGetConstBuffer(inst, env, host_info, block); + }, [&](IR::Value operand) { return ResolveConditionalPhi(operand, block); }); } std::optional TryGetConstant(IR::Value& value, Environment& env) { @@ -339,13 +393,13 @@ std::optional TryGetConstant(IR::Value& value, Environment& env) { return ReadCbufCached(env, index_number, offset_number); } -std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) { +std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) { switch (inst->GetOpcode()) { default: return std::nullopt; case IR::Opcode::BitwiseOr32: { - std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; - std::optional rhs{TrackCached(inst->Arg(1), env, host_info)}; + std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)}; + std::optional rhs{TrackCached(inst->Arg(1), env, host_info, block)}; if (!lhs || !rhs) { return std::nullopt; } @@ -375,7 +429,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme if (!shift.IsImmediate()) { return std::nullopt; } - std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; + std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)}; if (lhs) { lhs->shift_left = shift.U32(); } @@ -403,7 +457,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme return std::nullopt; } while (false); } - std::optional lhs{TrackCached(op1, env, host_info)}; + std::optional lhs{TrackCached(op1, env, host_info, block)}; if (lhs) { lhs->shift_left = static_cast(std::countr_zero(op2.U32())); } @@ -469,7 +523,7 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) { ConstBufferAddr addr; if (IsBindless(inst)) { - const std::optional track_addr{TrackCached(inst.Arg(0), env, host_info)}; + const std::optional track_addr{TrackCached(inst.Arg(0), env, host_info, block)}; if (!track_addr) { throw NotImplementedException("Failed to track bindless texture constant buffer"); @@ -671,6 +725,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo env.track_cache.clear(); TextureInstVector to_replace; for (IR::Block* const block : program.post_order_blocks) { + env.track_cache.clear(); for (IR::Inst& inst : block->Instructions()) { if (!IsTextureInstruction(inst)) { continue; diff --git a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp index 08c658cb81..5842f6135b 100644 --- a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp +++ b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -76,4 +79,44 @@ void VendorWorkaroundPass(IR::Program& program) { } } +bool HasBrokenPattern(const IR::Program& program) { + if (program.stage != Stage::Fragment) { + return false; + } + for (const IR::Block* block : program.post_order_blocks) { + for (const IR::Inst& inst : block->Instructions()) { + switch (inst.GetOpcode()) { + case IR::Opcode::ShuffleIndex: + case IR::Opcode::ShuffleUp: + case IR::Opcode::ShuffleDown: + case IR::Opcode::ShuffleButterfly: + break; + default: + continue; + } + const IR::Value shuffle_arg{inst.Arg(0)}; + if (shuffle_arg.IsImmediate()) { + continue; + } + const IR::Inst* bitcast{shuffle_arg.InstRecursive()}; + if (!bitcast || bitcast->GetOpcode() != IR::Opcode::BitCastU32F32) { + continue; + } + const IR::Value bitcast_arg{bitcast->Arg(0)}; + if (bitcast_arg.IsImmediate()) { + continue; + } + const IR::Inst* attribute{bitcast_arg.InstRecursive()}; + if (!attribute || attribute->GetOpcode() != IR::Opcode::GetAttribute) { + continue; + } + const IR::Attribute attr{attribute->Arg(0).Attribute()}; + if (attr == IR::Attribute::PositionX || attr == IR::Attribute::PositionY) { + return true; + } + } + } + return false; +} + } // namespace Shader::Optimization diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index b8d760a30e..71309e040e 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -172,7 +172,6 @@ struct StorageBufferDescriptor { u32 cbuf_offset; u32 count; bool is_written; - bool is_global_fallback{}; auto operator<=>(const StorageBufferDescriptor&) const = default; }; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 96facedf79..64d14ab2a6 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -22,7 +22,6 @@ add_library(video_core STATIC buffer_cache/usage_tracker.h buffer_cache/virtual_range_cache.h buffer_cache/word_manager.h - cache_reclaim.h cache_types.h capture.h cdma_pusher.cpp @@ -168,8 +167,6 @@ add_library(video_core STATIC renderer_vulkan/vk_fence_manager.h renderer_vulkan/vk_graphics_pipeline.cpp renderer_vulkan/vk_graphics_pipeline.h - renderer_vulkan/vk_guest_memory.cpp - renderer_vulkan/vk_guest_memory.h renderer_vulkan/vk_multi_range_buffer.cpp renderer_vulkan/vk_multi_range_buffer.h renderer_vulkan/vk_master_semaphore.cpp @@ -259,6 +256,8 @@ add_library(video_core STATIC texture_cache/util.h textures/astc.h textures/astc.cpp + textures/bcn.cpp + textures/bcn.h textures/decoders.cpp textures/decoders.h textures/texture.cpp @@ -407,7 +406,7 @@ if (ENABLE_OPENGL) endif() target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PUBLIC shader_recompiler bc_decoder gpu_logging) +target_link_libraries(video_core PUBLIC shader_recompiler stb bc_decoder gpu_logging) if (ENABLE_OPENGL) target_link_libraries(video_core PUBLIC glad) endif() diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index cdfb235ef0..d7eb21612f 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -129,49 +129,13 @@ public: write_tick = write_tick_; } - u64 ContentSerial() const noexcept { - return content_serial; - } - - void MarkContentModified() noexcept { - content_serial = ++next_content_serial; - } - - [[nodiscard]] bool HasDrawHazard(u64 pass, u64 wfi, DAddr addr, u64 size, - bool check_feedback) const noexcept { - return draw_write_pass == pass && - (draw_write_wfi < wfi || (check_feedback && draw_write_feedback)) && - addr < draw_write_end && addr + size > draw_write_begin; - } - - void MarkDrawWrite(u64 pass, u64 wfi, DAddr addr, u64 size, bool feedback) noexcept { - if (draw_write_pass != pass) { - draw_write_pass = pass; - draw_write_begin = addr; - draw_write_end = addr + size; - draw_write_feedback = false; - } - draw_write_wfi = wfi; - draw_write_feedback |= feedback; - draw_write_begin = (std::min)(draw_write_begin, addr); - draw_write_end = (std::max)(draw_write_end, addr + size); - } - private: - static inline u64 next_content_serial = 0; - VAddr cpu_addr = 0; BufferFlagBits flags{}; int stream_score = 0; size_t lru_id = SIZE_MAX; size_t size_bytes = 0; u64 write_tick = 0; - u64 content_serial = ++next_content_serial; - u64 draw_write_pass = 0; - u64 draw_write_wfi = 0; - DAddr draw_write_begin = 0; - DAddr draw_write_end = 0; - bool draw_write_feedback = false; }; } // namespace VideoCommon diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 8f7ca3fe3c..f30c85911f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -30,54 +30,34 @@ BufferCache

::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R #ifdef YUZU_LEGACY immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive); #endif - device_local_memory = runtime.GetDeviceLocalMemory(); - const auto thresholds = VideoCommon::MakeReclaimThresholds( - device_local_memory, static_cast(TARGET_THRESHOLD), - static_cast(DEFAULT_EXPECTED_MEMORY), static_cast(DEFAULT_CRITICAL_MEMORY), - HEAP_PRESSURE_HEADROOM); - minimum_memory = thresholds.minimum; - expected_memory = thresholds.expected; - critical_memory = thresholds.critical; - heap_headroom = thresholds.headroom; + if (!runtime.CanReportMemoryUsage()) { + minimum_memory = DEFAULT_EXPECTED_MEMORY; + critical_memory = DEFAULT_CRITICAL_MEMORY; + return; + } + + const s64 device_local_memory = static_cast(runtime.GetDeviceLocalMemory()); + const s64 min_spacing_expected = device_local_memory - 1_GiB; + const s64 min_spacing_critical = device_local_memory - 512_MiB; + const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD); + const s64 min_vacancy_expected = (6 * mem_threshold) / 10; + const s64 min_vacancy_critical = (2 * mem_threshold) / 10; + minimum_memory = static_cast( + (std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected), + DEFAULT_EXPECTED_MEMORY)); + critical_memory = static_cast( + (std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical), + DEFAULT_CRITICAL_MEMORY)); } template BufferCache

::~BufferCache() = default; -template -void BufferCache

::ReclaimInline() { - if (total_used_memory < minimum_memory) { - return; - } - int num_iterations = 8; - const auto clean_up = [this, &num_iterations](BufferId buffer_id) { - if (num_iterations == 0) { - return true; - } - --num_iterations; - Buffer& buffer = slot_buffers[buffer_id]; - if (memory_tracker.IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) { - return false; - } - DeleteBuffer(buffer_id); - return false; - }; - lru_cache.ForEachItemBelow(frame_tick - INLINE_TICKS_TO_DESTROY, clean_up); -} - template void BufferCache

::RunGarbageCollector() { - const bool aggressive_gc = heap_pressure || total_used_memory >= critical_memory; - const bool priority_gc = aggressive_gc || total_used_memory >= expected_memory; - u64 ticks_to_destroy = 120; - int num_iterations = 32; - if (aggressive_gc) { - ticks_to_destroy = 30; - num_iterations = 64; - } else if (priority_gc) { - ticks_to_destroy = 60; - num_iterations = 48; - } + const bool aggressive_gc = total_used_memory >= critical_memory; + const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; + int num_iterations = aggressive_gc ? 64 : 32; const auto clean_up = [this, &num_iterations](BufferId buffer_id) { if (num_iterations == 0) { return true; @@ -116,11 +96,11 @@ void BufferCache

::TickFrame() { const bool skip_preferred = hits * 256 < shots * 251; channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; - heap_pressure = false; - if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) { - heap_pressure = runtime.GetDeviceMemoryUsage() + heap_headroom >= device_local_memory; + // If we can obtain the memory info, use it instead of the estimate. + if (runtime.CanReportMemoryUsage()) { + total_used_memory = runtime.GetDeviceMemoryUsage(); } - if (total_used_memory >= minimum_memory || heap_pressure) { + if (total_used_memory >= minimum_memory) { RunGarbageCollector(); } ++frame_tick; @@ -141,7 +121,7 @@ void BufferCache

::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size template void BufferCache

::WriteMemory(DAddr device_addr, u64 size) { - if (memory_tracker.IsRegionGpuModified(device_addr, size)) { + if (IsRegionGpuModified(device_addr, size)) { ClearDownload(device_addr, size); gpu_modified_ranges.Subtract(device_addr, size); } @@ -266,10 +246,13 @@ bool BufferCache

::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am const auto& copy = copies[0]; src_buffer.MarkUsage(copy.src_offset, copy.size); dest_buffer.MarkUsage(copy.dst_offset, copy.size); - dest_buffer.MarkContentModified(); runtime.CopyBuffer(dest_buffer, src_buffer, copies, true); if (has_new_downloads) { memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); + const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate(); + if (should_sync) { + runtime.Finish(); + } } Tegra::Memory::DeviceGuestMemoryScoped @@ -298,7 +281,6 @@ bool BufferCache

::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { const u32 offset = dest_buffer.Offset(*cpu_dst_address); runtime.ClearBuffer(dest_buffer, offset, size, value); dest_buffer.MarkUsage(offset, size); - dest_buffer.MarkContentModified(); return true; } @@ -327,18 +309,14 @@ std::pair BufferCache

::ObtainCPUBuffer( default: break; } - buffer.MarkUsage(buffer.Offset(device_addr), size); switch (post_op) { case ObtainBufferOperation::MarkAsWritten: MarkWrittenBuffer(buffer_id, device_addr, size); break; case ObtainBufferOperation::DiscardWrite: { - const DAddr device_addr_start = Common::AlignDown(device_addr, 64); - const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); - const size_t new_size = device_addr_end - device_addr_start; - ClearDownload(device_addr_start, new_size); - gpu_modified_ranges.Subtract(device_addr_start, new_size); + ClearDownload(device_addr, size); + gpu_modified_ranges.Subtract(device_addr, size); break; } default: @@ -367,45 +345,14 @@ void BufferCache

::DisableGraphicsUniformBuffer(size_t stage, u32 index) { template void BufferCache

::UpdateGraphicsBuffers(bool is_indexed) { - if constexpr (!IS_OPENGL) { - draw_writes.clear(); - draw_pass = runtime.RenderPassSerial(); - draw_wfi = runtime.WaitForIdleSerial(); - draw_hazard = false; - recording_draw = true; - } - ReclaimInline(); do { channel_state->has_deleted_buffers = false; DoUpdateGraphicsBuffers(is_indexed); } while (channel_state->has_deleted_buffers); } -template -bool BufferCache

::TakeDrawHazard() noexcept { - return std::exchange(draw_hazard, false); -} - -template -void BufferCache

::CommitDrawWrites() { - if constexpr (!IS_OPENGL) { - recording_draw = false; - if (draw_writes.empty()) { - return; - } - const u64 pass = runtime.RenderPassSerial(); - for (const DrawWrite& write : draw_writes) { - Buffer& buffer = slot_buffers[write.buffer_id]; - buffer.setWriteTick(runtime.CurrentTick()); - buffer.MarkDrawWrite(pass, draw_wfi, write.device_addr, write.size, write.feedback); - } - runtime.MarkRenderPassWrites(); - } -} - template void BufferCache

::UpdateComputeBuffers() { - ReclaimInline(); do { channel_state->has_deleted_buffers = false; DoUpdateComputeBuffers(); @@ -802,35 +749,17 @@ void BufferCache

::BindHostIndexBuffer() { const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr); const u32 size = channel_state->index_buffer.size; const auto& draw_state = maxwell3d->draw_manager.draw_state; - if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - if (draw_state.topology == Maxwell::PrimitiveTopology::Quads || - draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { - const DAddr device_addr = channel_state->index_buffer.device_addr; - std::span indices = draw_state.inline_index_draw_indexes; - if (indices.empty() && !IsRegionGpuModified(device_addr, size)) { - indices = ImmediateBufferWithData(device_addr, size); - } - if (runtime.BindQuadIndices(draw_state.topology, draw_state.index_buffer.format, - draw_state.index_buffer.first, - draw_state.index_buffer.count, indices)) { - return; - } - } - } if (draw_state.inline_index_draw_indexes.empty()) { SynchronizeBuffer(buffer, channel_state->index_buffer.device_addr, size); - } else if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { - const auto upload_staging = runtime.UploadStagingBuffer(size); - std::memcpy(upload_staging.mapped_span.data(), - draw_state.inline_index_draw_indexes.data(), size); - runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, - draw_state.index_buffer.first, draw_state.index_buffer.count, - upload_staging.buffer, static_cast(upload_staging.offset), - size); - return; } else { - buffer.MarkContentModified(); - buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); + if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { + auto upload_staging = runtime.UploadStagingBuffer(size); + std::array copies{{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; + std::memcpy(upload_staging.mapped_span.data(), draw_state.inline_index_draw_indexes.data(), size); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true); + } else { + buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); + } } if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { const u32 new_offset = offset + draw_state.index_buffer.first * u32(draw_state.index_buffer.FormatSizeInBytes()); @@ -975,7 +904,6 @@ void BufferCache

::BindHostDrawIndirectBuffers() { Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); SynchronizeBuffer(buffer, binding.device_addr, binding.size); - buffer.MarkUsage(buffer.Offset(binding.device_addr), binding.size); }; if (current_draw_indirect->include_count) { bind_buffer(channel_state->count_buffer_binding); @@ -1079,14 +1007,15 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } template -void BufferCache

::ResolveMultiRangeStorage(Binding& binding, - std::vector& pool, - bool is_written) { +void BufferCache

::ResolveMultiRangeStorage(Binding& binding, bool is_written, + std::vector& pool) { binding.segment_first = 0; binding.segment_count = 0; if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) { - if (binding.gpu_addr == 0 || binding.size == 0 || - (is_written && !runtime.PrefersSparseSources())) { + if (binding.gpu_addr == 0 || binding.size == 0) { + return; + } + if (is_written && !runtime.PrefersSparseSources()) { return; } const VirtualSegments* found = @@ -1094,7 +1023,7 @@ void BufferCache

::ResolveMultiRangeStorage(Binding& binding, if (!found || found->size() < 2) { return; } - const VirtualSegments& segments = *found; + const VirtualSegments segments = *found; const u32 first = static_cast(pool.size()); const bool prefer_sparse = runtime.PrefersSparseSources(); for (const VirtualSegment& segment : segments) { @@ -1131,7 +1060,9 @@ bool BufferCache

::BindMultiRangeStorage(const Binding& binding, bool is_writt const MultiRangeSegment& segment = pool[binding.segment_first + index]; Buffer& buffer = slot_buffers[segment.buffer_id]; TouchBuffer(buffer, segment.buffer_id); - SynchronizeBuffer(buffer, segment.device_addr, segment.size); + if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) { + runtime.InvalidateMultiRange(key); + } const u32 offset = buffer.Offset(segment.device_addr); buffer.MarkUsage(offset, segment.size); if (is_written) { @@ -1222,8 +1153,8 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); size = binding.size; - SynchronizeBuffer(buffer, binding.device_addr, size, false); - MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true); + SynchronizeBuffer(buffer, binding.device_addr, size); + MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, size); host_buffer = &buffer; @@ -1303,7 +1234,7 @@ void BufferCache

::BindHostComputeStorageBuffers() { buffer.MarkUsage(offset, size); if (is_written) { - MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); + MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true); } if constexpr (NEEDS_BIND_STORAGE_INDEX) { @@ -1387,15 +1318,13 @@ void BufferCache

::UpdateIndexBuffer() { flags[Dirty::IndexBuffer] = false; if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] { auto inline_index_size = static_cast(draw_state.inline_index_draw_indexes.size()); - if constexpr (!USE_MEMORY_MAPS_FOR_UPLOADS) { - const u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE); - if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] { - inline_buffer_id = CreateBuffer(0, buffer_size, false); - } - if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] { - DeleteBuffer(inline_buffer_id, true); - inline_buffer_id = CreateBuffer(0, buffer_size, false); - } + u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE); + if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] { + inline_buffer_id = CreateBuffer(0, buffer_size, false); + } + if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] { + slot_buffers.erase(inline_buffer_id); + inline_buffer_id = CreateBuffer(0, buffer_size, false); } channel_state->index_buffer = Binding{ .device_addr = 0, @@ -1509,12 +1438,10 @@ void BufferCache

::UpdateStorageBuffers(size_t stage) { ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { // Resolve buffer Binding& binding = channel_state->storage_buffers[stage][index]; + const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size, false); + binding.buffer_id = buffer_id; const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; - ResolveMultiRangeStorage(binding, graphics_segments, is_written); - binding.buffer_id = NULL_BUFFER_ID; - if (binding.segment_count == 0 || is_written) { - binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); - } + ResolveMultiRangeStorage(binding, is_written, graphics_segments); }); } @@ -1577,12 +1504,10 @@ void BufferCache

::UpdateComputeStorageBuffers() { ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { // Resolve buffer Binding& binding = channel_state->compute_storage_buffers[index]; - const bool is_written = ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; - ResolveMultiRangeStorage(binding, compute_segments, is_written); - binding.buffer_id = NULL_BUFFER_ID; - if (binding.segment_count == 0 || is_written) { - binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); - } + binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false); + const bool is_written = + ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; + ResolveMultiRangeStorage(binding, is_written, compute_segments); }); } @@ -1595,14 +1520,11 @@ void BufferCache

::UpdateComputeTextureBuffers() { } template -void BufferCache

::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, - bool feedback) { +void BufferCache

::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) { if constexpr (!IS_OPENGL) { - Buffer& buffer = slot_buffers[buffer_id]; - buffer.setWriteTick(runtime.CurrentTick()); - buffer.MarkContentModified(); - if (recording_draw) { - draw_writes.push_back({buffer_id, device_addr, size, feedback}); + if (needs_sync) { + Buffer& buffer = slot_buffers[buffer_id]; + buffer.setWriteTick(runtime.CurrentTick()); } } memory_tracker.MarkRegionAsGpuModified(device_addr, size); @@ -1619,8 +1541,11 @@ BufferId BufferCache

::FindBuffer(DAddr device_addr, u32 size, bool sparse_com const BufferId buffer_id = page_table[page]; if (buffer_id) { Buffer& buffer = slot_buffers[buffer_id]; - WaitForGpuFenceIfNeeded(buffer); if (buffer.IsInBounds(device_addr, size)) { + const bool should_sync = Settings::IsGPUFenceBehaviorAccurate(); + if (should_sync) { + SynchronizeBufferWrites(buffer); + } bool usable = true; if constexpr (requires { buffer.IsSparseCompatible(); }) { if (sparse_compatible && !buffer.IsSparseCompatible()) { @@ -1636,17 +1561,11 @@ BufferId BufferCache

::FindBuffer(DAddr device_addr, u32 size, bool sparse_com } template -void BufferCache

::WaitForGpuFenceIfNeeded(Buffer& buffer) { +void BufferCache

::SynchronizeBufferWrites(Buffer& buffer) { if constexpr (!IS_OPENGL) { - const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate(); - const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict(); - if (gpu_fence_accurate || gpu_fence_strict) { - const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3; - const u64 buffer_tick = buffer.getWriteTick(); - const u64 gpu_tick = runtime.KnownGpuTick(); - if (buffer_tick > gpu_tick + gpu_tick_delay) { - runtime.Wait(buffer_tick); - } + const u64 buffer_tick = buffer.getWriteTick(); + if (!runtime.IsFree(buffer_tick)) { + runtime.Wait(buffer_tick); } } } @@ -1745,7 +1664,7 @@ void BufferCache

::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, .size = overlap.SizeBytes(), }); new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size); - runtime.CopyBuffer(new_buffer, overlap, FixSmallVectorADL(copies), true); + runtime.CopyBuffer(new_buffer, overlap, copies, true); #ifdef YUZU_LEGACY if (immediately_free) runtime.Finish(); @@ -1761,11 +1680,6 @@ BufferId BufferCache

::CreateBuffer(DAddr device_addr, u32 wanted_size, wanted_size = static_cast(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); - if constexpr (requires(Buffer& buffer) { buffer.IsSparseCompatible(); }) { - for (const BufferId overlap_id : overlap.ids) { - sparse_compatible |= slot_buffers[overlap_id].IsSparseCompatible(); - } - } const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size, sparse_compatible); auto& new_buffer = slot_buffers[new_buffer_id]; @@ -1823,40 +1737,70 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { } template -bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size, - bool check_feedback) { - if constexpr (!IS_OPENGL) { - draw_hazard |= - buffer.HasDrawHazard(draw_pass, draw_wfi, device_addr, size, check_feedback); - } - upload_copies.clear(); - u64 total_size_bytes = 0; - u64 largest_copy = 0; - const DAddr buffer_start = buffer.cpu_addr_cached; - memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { - upload_copies.push_back(BufferCopy{ - .src_offset = total_size_bytes, - .dst_offset = device_addr_out - buffer_start, - .size = range_size, +bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) { +#ifdef __ANDROID__ + const bool use_optimized_vertex_buffers = Settings::values.use_optimized_vertex_buffers.GetValue(); +#else + constexpr bool use_optimized_vertex_buffers = true; +#endif + + if (use_optimized_vertex_buffers) { + upload_copies.clear(); + u64 total_size_bytes = 0; + u64 largest_copy = 0; + const DAddr buffer_start = buffer.cpu_addr_cached; + const auto add_upload = [&](DAddr start, DAddr end) { + if (start == end) return; + const u64 range_size = end - start; + upload_copies.push_back(BufferCopy{ + .src_offset = total_size_bytes, + .dst_offset = start - buffer_start, + .size = range_size, + }); + total_size_bytes += range_size; + largest_copy = (std::max)(largest_copy, range_size); + }; + memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { + DAddr upload_start = device_addr_out; + gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) { + add_upload(upload_start, gpu_start); + upload_start = gpu_end; + }); + add_upload(upload_start, device_addr_out + range_size); }); - total_size_bytes += range_size; - largest_copy = (std::max)(largest_copy, range_size); - }); - if (total_size_bytes == 0) { - return true; - } - const std::span copies_span(upload_copies.data(), upload_copies.size()); - UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); - if constexpr (IS_OPENGL) { + if (total_size_bytes == 0) { + return true; + } + const std::span copies_span(upload_copies.data(), upload_copies.size()); + UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); any_buffer_uploaded = true; + return false; + } else { + boost::container::small_vector copies; + u64 total_size_bytes = 0; + u64 largest_copy = 0; + DAddr buffer_start = buffer.CpuAddr(); + memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { + copies.push_back(BufferCopy{ + .src_offset = total_size_bytes, + .dst_offset = device_addr_out - buffer_start, + .size = range_size, + }); + total_size_bytes += range_size; + largest_copy = (std::max)(largest_copy, range_size); + }); + if (total_size_bytes == 0) { + return true; + } + const std::span copies_span(copies.data(), copies.size()); + UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); + return false; } - return false; } template void BufferCache

::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span copies) { - buffer.MarkContentModified(); if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { MappedUploadMemory(buffer, total_size_bytes, copies); } else { @@ -1882,9 +1826,6 @@ void BufferCache

::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer, if (immediate_buffer.empty()) { immediate_buffer = ImmediateBuffer(largest_copy); } - if (Settings::values.enable_gpu_buffer_readback.GetValue()) { - DownloadBufferMemory(buffer, device_addr, copy.size); - } device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size); upload_span = immediate_buffer.subspan(0, copy.size); } @@ -1898,28 +1839,11 @@ void BufferCache

::MappedUploadMemory([[maybe_unused]] Buffer& buffer, [[maybe_unused]] u64 total_size_bytes, [[maybe_unused]] std::span copies) { if constexpr (USE_MEMORY_MAPS) { - if constexpr (requires { runtime.DirectUploadSpan(buffer, copies); }) { - const std::span direct = runtime.DirectUploadSpan(buffer, copies); - if (!direct.empty()) { - for (const BufferCopy& copy : copies) { - const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; - if (Settings::values.enable_gpu_buffer_readback.GetValue()) { - DownloadBufferMemory(buffer, device_addr, copy.size); - } - device_memory.ReadBlockUnsafe(device_addr, direct.data() + copy.dst_offset, - copy.size); - } - return; - } - } auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes); const std::span staging_pointer = upload_staging.mapped_span; for (BufferCopy& copy : copies) { u8* const src_pointer = staging_pointer.data() + copy.src_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; - if (Settings::values.enable_gpu_buffer_readback.GetValue()) { - DownloadBufferMemory(buffer, device_addr, copy.size); - } device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size); // Apply the staging offset copy.src_offset += upload_staging.offset; @@ -1956,7 +1880,6 @@ void BufferCache

::InlineMemoryImplementation(DAddr dest_address, size_t copy_ BufferId buffer_id = FindBuffer(dest_address, static_cast(copy_size), false); auto& buffer = slot_buffers[buffer_id]; SynchronizeBuffer(buffer, dest_address, static_cast(copy_size)); - buffer.MarkContentModified(); if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { auto upload_staging = runtime.UploadStagingBuffer(copy_size); @@ -2011,16 +1934,6 @@ void BufferCache

::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64 } if constexpr (USE_MEMORY_MAPS) { - if constexpr (requires { runtime.DirectDownloadSpan(buffer); }) { - const std::span direct = runtime.DirectDownloadSpan(buffer); - if (!direct.empty()) { - for (const BufferCopy& copy : copies) { - device_memory.WriteBlockUnsafe(buffer.CpuAddr() + copy.src_offset, - direct.data() + copy.src_offset, copy.size); - } - return; - } - } auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); const u8* const mapped_memory = download_staging.mapped_span.data(); const std::span copies_span(copies.data(), copies.data() + copies.size()); @@ -2089,10 +2002,6 @@ void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes()); } - if (inline_buffer_id == buffer_id) { - inline_buffer_id = NULL_BUFFER_ID; - } - Unregister(buffer_id); #ifdef YUZU_LEGACY diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index d1f9ff5f97..26bf3dd9ed 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -31,7 +31,6 @@ #include "video_core/buffer_cache/buffer_base.h" #include "video_core/buffer_cache/virtual_range_cache.h" #include "video_core/control/channel_state_cache.h" -#include "video_core/cache_reclaim.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" #include "video_core/engines/maxwell_3d.h" @@ -200,8 +199,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches pool); - - - - - void ResolveMultiRangeStorage(Binding& binding, std::vector& pool, - bool is_written); + void ResolveMultiRangeStorage(Binding& binding, bool is_written, + std::vector& pool); void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size); @@ -260,10 +253,6 @@ public: void UpdateComputeBuffers(); - [[nodiscard]] bool TakeDrawHazard() noexcept; - - void CommitDrawWrites(); - void BindHostGeometryBuffers(bool is_indexed); void BindHostStageBuffers(size_t stage); @@ -389,8 +378,6 @@ private: void RunGarbageCollector(); - void ReclaimInline(); - void BindHostIndexBuffer(); void BindHostVertexBuffers(); @@ -443,12 +430,11 @@ private: void UpdateComputeTextureBuffers(); - void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, - bool feedback = false); + void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false); [[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible); - void WaitForGpuFenceIfNeeded(Buffer& buffer); + void SynchronizeBufferWrites(Buffer& buffer); [[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size); @@ -466,8 +452,7 @@ private: void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept; - bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size, - bool check_feedback = true); + bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size); void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span copies); @@ -526,18 +511,6 @@ private: boost::container::small_vector upload_copies; - struct DrawWrite { - BufferId buffer_id; - DAddr device_addr; - u32 size; - bool feedback; - }; - boost::container::small_vector draw_writes; - u64 draw_pass = 0; - u64 draw_wfi = 0; - bool draw_hazard = false; - bool recording_draw = false; - MemoryTracker memory_tracker; Common::RangeSet uncommitted_gpu_modified_ranges; Common::RangeSet gpu_modified_ranges; @@ -564,12 +537,8 @@ private: std::vector compute_segments; u64 frame_tick = 0; u64 total_used_memory = 0; - u64 device_local_memory = 0; u64 minimum_memory = 0; - u64 expected_memory = 0; u64 critical_memory = 0; - u64 heap_headroom = 0; - bool heap_pressure = false; BufferId inline_buffer_id; #ifdef YUZU_LEGACY bool immediately_free = false; diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h index 1b3ab2f998..6f84e463e3 100644 --- a/src/video_core/buffer_cache/usage_tracker.h +++ b/src/video_core/buffer_cache/usage_tracker.h @@ -6,9 +6,7 @@ #pragma once -#include -#include - +#include "common/alignment.h" #include "common/common_types.h" namespace VideoCommon { @@ -17,77 +15,74 @@ class UsageTracker { // PAGE_SHIFT is a macro on FreeBSD static constexpr size_t BUFFER_BYTES_PER_BITSHIFT = 6; static constexpr size_t BUFFER_PAGE_SHIFT = 6 + BUFFER_BYTES_PER_BITSHIFT; - static constexpr u64 BUFFER_PAGE_BYTES = u64{1} << BUFFER_PAGE_SHIFT; - - struct Page { - u64 bits; - u64 tick; - }; - + static constexpr size_t BUFFER_PAGE_BYTES = 1 << BUFFER_PAGE_SHIFT; public: - explicit UsageTracker(size_t size) - : pages((size >> BUFFER_PAGE_SHIFT) + 1), full_ticks(pages.size()) {} - - void Track(u64 offset, u64 size, u64 tick, u64 gpu_tick) noexcept { - const u64 end = offset + size; - if (size == 0 || ((end - 1) >> BUFFER_PAGE_SHIFT) >= pages.size()) { - return; - } - const u64 first_page = offset >> BUFFER_PAGE_SHIFT; - const u64 last_page = (end - 1) >> BUFFER_PAGE_SHIFT; - TrackPage(first_page, PageMask(first_page, offset, end), tick, gpu_tick); - if (last_page == first_page) { - return; - } - std::fill(full_ticks.begin() + first_page + 1, full_ticks.begin() + last_page, tick); - TrackPage(last_page, PageMask(last_page, offset, end), tick, gpu_tick); + explicit UsageTracker(size_t size) { + const size_t num_pages = (size >> BUFFER_PAGE_SHIFT) + 1; + pages.resize(num_pages, 0ULL); } - [[nodiscard]] bool IsUsed(u64 offset, u64 size, u64 gpu_tick) const noexcept { - const u64 end = offset + size; - if (size == 0 || ((end - 1) >> BUFFER_PAGE_SHIFT) >= pages.size()) { + void Reset() noexcept { + std::ranges::fill(pages, 0ULL); + } + + void Track(u64 offset, u64 size) noexcept { + const size_t page = offset >> BUFFER_PAGE_SHIFT; + const size_t page_end = (offset + size) >> BUFFER_PAGE_SHIFT; + if (page_end < page || page_end >= pages.size()) { + return; + } + TrackPage(page, offset, size); + if (page == page_end) { + return; + } + for (size_t i = page + 1; i < page_end; i++) { + pages[i] = ~u64{0}; + } + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, BUFFER_PAGE_BYTES); + TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); + } + + [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept { + const size_t page = offset >> BUFFER_PAGE_SHIFT; + const size_t page_end = (offset + size) >> BUFFER_PAGE_SHIFT; + if (page_end < page || page_end >= pages.size()) { return false; } - const u64 first_page = offset >> BUFFER_PAGE_SHIFT; - const u64 last_page = (end - 1) >> BUFFER_PAGE_SHIFT; - if (IsPageUsed(first_page, PageMask(first_page, offset, end), gpu_tick) || - IsPageUsed(last_page, PageMask(last_page, offset, end), gpu_tick)) { + if (IsPageUsed(page, offset, size)) { return true; } - for (u64 page = first_page + 1; page < last_page; ++page) { - if (IsPageUsed(page, ~u64{0}, gpu_tick)) { + for (size_t i = page + 1; i < page_end; i++) { + if (pages[i] != 0) { return true; } } - return false; + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, BUFFER_PAGE_BYTES); + return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); } private: - void TrackPage(u64 page, u64 mask, u64 tick, u64 gpu_tick) noexcept { - Page& entry = pages[page]; - if (entry.tick <= gpu_tick) { - entry.bits = 0; - } - entry.bits |= mask; - entry.tick = (std::max)(entry.tick, tick); + void TrackPage(u64 page, u64 offset, u64 size) noexcept { + const size_t offset_in_page = offset % BUFFER_PAGE_BYTES; + const size_t first_bit = offset_in_page >> BUFFER_BYTES_PER_BITSHIFT; + const size_t num_bits = std::min(size, BUFFER_PAGE_BYTES) >> BUFFER_BYTES_PER_BITSHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + pages[page] |= (~u64{0} & mask) << first_bit; } - [[nodiscard]] bool IsPageUsed(u64 page, u64 mask, u64 gpu_tick) const noexcept { - const Page& entry = pages[page]; - return full_ticks[page] > gpu_tick || (entry.tick > gpu_tick && (entry.bits & mask) != 0); + bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { + const size_t offset_in_page = offset % BUFFER_PAGE_BYTES; + const size_t first_bit = offset_in_page >> BUFFER_BYTES_PER_BITSHIFT; + const size_t num_bits = std::min(size, BUFFER_PAGE_BYTES) >> BUFFER_BYTES_PER_BITSHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + const size_t mask2 = (~u64{0} & mask) << first_bit; + return (pages[page] & mask2) != 0; } - [[nodiscard]] static u64 PageMask(u64 page, u64 offset, u64 end) noexcept { - const u64 page_begin = page << BUFFER_PAGE_SHIFT; - const u64 first = - ((std::max)(offset, page_begin) - page_begin) >> BUFFER_BYTES_PER_BITSHIFT; - const u64 last = ((std::min)(end, page_begin + BUFFER_PAGE_BYTES) - 1 - page_begin) >> - BUFFER_BYTES_PER_BITSHIFT; - return (~u64{0} >> (63 - last)) & (~u64{0} << first); - } - - std::vector pages; - std::vector full_ticks; +private: + std::vector pages; }; } // namespace VideoCommon diff --git a/src/video_core/buffer_cache/virtual_range_cache.h b/src/video_core/buffer_cache/virtual_range_cache.h index 542fb52b23..f4284d6abb 100644 --- a/src/video_core/buffer_cache/virtual_range_cache.h +++ b/src/video_core/buffer_cache/virtual_range_cache.h @@ -126,11 +126,11 @@ private: } void ApplyDeferred() { + std::vector pending; bool overflow = false; { std::scoped_lock lock{deferred_mutex}; has_deferred.store(false, std::memory_order_release); - pending.clear(); pending.swap(deferred); overflow = deferred_overflow; deferred_overflow = false; @@ -165,7 +165,6 @@ private: ::Common::unordered_map entries; std::vector deferred; - std::vector pending; std::mutex deferred_mutex; std::atomic has_deferred{false}; bool deferred_overflow{}; diff --git a/src/video_core/cache_reclaim.h b/src/video_core/cache_reclaim.h deleted file mode 100644 index a8870ea96b..0000000000 --- a/src/video_core/cache_reclaim.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include "common/common_types.h" - -namespace VideoCommon { - -struct ReclaimThresholds { - u64 minimum{}; - u64 expected{}; - u64 critical{}; - u64 headroom{}; -}; - -[[nodiscard]] constexpr ReclaimThresholds MakeReclaimThresholds(u64 device_local_memory, - u64 target_threshold, - u64 default_expected, - u64 default_critical, - u64 default_headroom) { - u64 critical = default_critical; - if (device_local_memory != 0) { - const u64 budget = (std::min)(device_local_memory, target_threshold); - critical = (std::min)(critical, budget / 2); - } - const u64 expected = (std::min)(default_expected, (critical * 3) / 4); - return ReclaimThresholds{ - .minimum = (expected * 3) / 4, - .expected = expected, - .critical = critical, - .headroom = (std::clamp)(device_local_memory / 4, default_headroom, default_headroom * 2), - }; -} - -} // namespace VideoCommon diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 1eb665ad79..2c0ba007ef 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -16,7 +16,7 @@ namespace Tegra { constexpr u32 MacroRegistersStart = 0xE00; -[[maybe_unused]] constexpr u32 ComputeInline = 0x6D; +constexpr u32 ComputeInline = 0x6D; DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) : system{system_} @@ -56,7 +56,7 @@ bool DmaPusher::Step() { return true; } - if (command_list_size == 0) { + if (prefetch_size > 0) { ProcessCommands(command_list.prefetch_command_list); dma_pushbuffer.pop(); return true; @@ -73,11 +73,16 @@ bool DmaPusher::Step() { synced = false; } - if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) { - subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32)); - } - if (header.size > 0) { + if (subchannels[dma_state.subchannel] && dma_state.method_count) { + const auto engine = subchannel_type[dma_state.subchannel]; + const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing; + const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart; + if (kepler_payload || macro_payload) { + const size_t words = std::min(dma_state.method_count, header.size); + subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32)); + } + } const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe(); if (use_safe) { Tegra::Memory::GpuGuestMemoryheaders(memory_manager, dma_state.dma_get, header.size, &command_headers); @@ -89,7 +94,6 @@ bool DmaPusher::Step() { } if (++dma_pushbuffer_subindex >= command_list_size) { - ProcessCommands(command_list.prefetch_command_list); dma_pushbuffer.pop(); dma_pushbuffer_subindex = 0; } else { diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index 0d3558a8f3..5891c25622 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp @@ -14,10 +14,6 @@ namespace Tegra::Engines { void Maxwell3D::DrawManager::ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument) { - if (draw_state.draw_mode == DrawMode::InstanceArray && - method != MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent)) { - DrawDeferred(maxwell3d); - } switch (method) { case MAXWELL3D_REG_INDEX(clear_surface): return Clear(maxwell3d, 1); @@ -76,20 +72,7 @@ void Maxwell3D::DrawManager::Clear(Maxwell3D& maxwell3d, u32 layer_count) { } } -void Maxwell3D::DrawManager::FlushInstanceArray(Maxwell3D& maxwell3d) { - if (draw_state.draw_mode != DrawMode::InstanceArray) { - return; - } - const u32 instance_count = draw_state.instance_count + 1; - draw_state.draw_mode = DrawMode::General; - draw_state.instance_count = 0; - if (maxwell3d.ShouldExecute()) { - maxwell3d.rasterizer->Draw(false, instance_count); - } -} - void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) { - FlushInstanceArray(maxwell3d); if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) { return; } @@ -98,7 +81,6 @@ void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) { } void Maxwell3D::DrawManager::DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances) { - FlushInstanceArray(maxwell3d); draw_state.topology = topology; draw_state.vertex_buffer.first = vertex_first; draw_state.vertex_buffer.count = vertex_count; @@ -107,29 +89,19 @@ void Maxwell3D::DrawManager::DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::Pr } void Maxwell3D::DrawManager::DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent) { - if (subsequent && draw_state.draw_mode == DrawMode::InstanceArray && - instance_topology == topology && draw_state.vertex_buffer.first == vertex_first && - draw_state.vertex_buffer.count == vertex_count) { - ++draw_state.instance_count; - return; - } - u32 base_instance = 0; - if (subsequent) { - base_instance = draw_state.base_instance + draw_state.instance_count + 1; - } - DrawDeferred(maxwell3d); - instance_topology = topology; draw_state.topology = topology; draw_state.vertex_buffer.first = vertex_first; draw_state.vertex_buffer.count = vertex_count; - draw_state.base_instance = base_instance; - draw_state.instance_count = 0; - draw_state.draw_mode = DrawMode::InstanceArray; - UpdateTopology(maxwell3d); + if (!subsequent) { + draw_state.instance_count = 1; + } + draw_state.base_instance = draw_state.instance_count - 1; + draw_state.draw_mode = DrawMode::Instance; + draw_state.instance_count++; + ProcessDraw(maxwell3d, false, 1); } void Maxwell3D::DrawManager::DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances) { - FlushInstanceArray(maxwell3d); draw_state.topology = topology; draw_state.index_buffer = maxwell3d.regs.index_buffer; draw_state.index_buffer.first = index_first; @@ -140,13 +112,11 @@ void Maxwell3D::DrawManager::DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::Pr } void Maxwell3D::DrawManager::DrawArrayIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology) { - FlushInstanceArray(maxwell3d); draw_state.topology = topology; ProcessDrawIndirect(maxwell3d); } void Maxwell3D::DrawManager::DrawIndexedIndirect(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count) { - FlushInstanceArray(maxwell3d); draw_state.topology = topology; draw_state.index_buffer = maxwell3d.regs.index_buffer; draw_state.index_buffer.first = index_first; @@ -216,9 +186,6 @@ void Maxwell3D::DrawManager::DrawBegin(Maxwell3D& maxwell3d) { void Maxwell3D::DrawManager::DrawEnd(Maxwell3D& maxwell3d, u32 instance_count, bool force_draw) { switch (draw_state.draw_mode) { - case DrawMode::InstanceArray: - FlushInstanceArray(maxwell3d); - break; case DrawMode::Instance: if (!force_draw) { break; @@ -282,68 +249,28 @@ void Maxwell3D::DrawManager::DrawTexture(Maxwell3D& maxwell3d) { } void Maxwell3D::DrawManager::UpdateTopology(Maxwell3D& maxwell3d) { - using Topology = Maxwell3D::Regs::PrimitiveTopology; - using Override = Maxwell3D::Regs::PrimitiveTopologyOverride; switch (maxwell3d.regs.primitive_topology_control) { case Maxwell3D::Regs::PrimitiveTopologyControl::UseInBeginMethods: break; case Maxwell3D::Regs::PrimitiveTopologyControl::UseSeparateState: switch (maxwell3d.regs.topology_override) { - case Override::None: + case Maxwell3D::Regs::PrimitiveTopologyOverride::None: break; - case Override::Points: - case Override::LegacyPoints: - draw_state.topology = Topology::Points; + case Maxwell3D::Regs::PrimitiveTopologyOverride::Points: + draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Points; break; - case Override::Lines: - case Override::LegacyLines: - case Override::LegacyLinesImm: - case Override::LegacyIndexedLines: - case Override::LegacyIndexedLines2: - draw_state.topology = Topology::Lines; + case Maxwell3D::Regs::PrimitiveTopologyOverride::Lines: + draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::Lines; break; - case Override::LineStrip: - case Override::LegacyLineStrip: - case Override::LegacyIndexedLineStrip: - draw_state.topology = Topology::LineStrip; + case Maxwell3D::Regs::PrimitiveTopologyOverride::LineStrip: + draw_state.topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip; break; - case Override::Triangles: - case Override::LegacyTriangles: - case Override::LegacyIndexedTriangles: - case Override::LegacyIndexedTriangles2: - draw_state.topology = Topology::Triangles; - break; - case Override::TriangleStrip: - case Override::LegacyTriangleStrip: - case Override::LegacyIndexedTriangleStrip: - draw_state.topology = Topology::TriangleStrip; - break; - case Override::LegacyTriangleFan: - case Override::LegacyTriangleFanImm: - case Override::LegacyIndexedTriangleFan: - draw_state.topology = Topology::TriangleFan; - break; - case Override::LinesAdjacency: - draw_state.topology = Topology::LinesAdjacency; - break; - case Override::LineStripAdjacency: - draw_state.topology = Topology::LineStripAdjacency; - break; - case Override::TrianglesAdjacency: - draw_state.topology = Topology::TrianglesAdjacency; - break; - case Override::TriangleStripAdjacency: - draw_state.topology = Topology::TriangleStripAdjacency; - break; - case Override::Patches: - draw_state.topology = Topology::Patches; + default: + draw_state.topology = Maxwell3D::Regs::PrimitiveTopology(maxwell3d.regs.topology_override); break; } break; } - if (u32(draw_state.topology) > u32(Topology::Patches)) { - draw_state.topology = Topology::Triangles; - } } void Maxwell3D::DrawManager::ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count) { diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 1ef6e0fc0d..96a12539a2 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -49,13 +49,16 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu case KEPLER_COMPUTE_REG_INDEX(exec_upload): { UploadInfo info{.upload_address = upload_address, .exec_address = upload_state.ExecTargetAddress(), - .copy_size = upload_state.GetUploadSize()}; + .copy_size = upload_state.GetUploadSize(), + .was_dirty = upload_dirty}; uploads.push_back(info); upload_state.ProcessExec(regs.exec_upload.linear != 0); break; } case KEPLER_COMPUTE_REG_INDEX(data_upload): { upload_address = current_dma_segment; + upload_dirty = current_dirty; + current_dirty = false; upload_state.ProcessData(method_argument, is_last_call); break; } @@ -64,9 +67,11 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu for (auto& data : uploads) { const GPUVAddr offset = data.exec_address - launch_desc_loc; - if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) && - memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) { - indirect_compute = {data.upload_address}; + if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) { + const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size); + if (data.was_dirty || source_dirty) { + indirect_compute = {data.upload_address}; + } } } uploads.clear(); @@ -83,6 +88,8 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32* switch (method) { case KEPLER_COMPUTE_REG_INDEX(data_upload): upload_address = current_dma_segment; + upload_dirty = current_dirty; + current_dirty = false; upload_state.ProcessData(base_start, amount); return; default: diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 4f7242f804..078d3d45be 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -226,11 +226,13 @@ private: VideoCore::RasterizerInterface* rasterizer = nullptr; Upload::State upload_state; GPUVAddr upload_address; + bool upload_dirty{}; struct UploadInfo { GPUVAddr upload_address; GPUVAddr exec_address; u32 copy_size; + bool was_dirty; }; std::vector uploads; std::optional indirect_compute{}; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 4adab42e67..e7da9d9138 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -588,7 +588,7 @@ void Maxwell3D::ProcessCounterReset() { void Maxwell3D::ProcessSyncPoint() { const u32 sync_point = regs.sync_info.sync_point.Value(); [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value(); - rasterizer->SignalSyncPoint(sync_point, 1); + rasterizer->SignalSyncPoint(sync_point); } void Maxwell3D::ProcessCBBind(size_t stage_index) { diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ae4b761ced..0cab2ae986 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -3034,7 +3034,7 @@ public: // clang-format on struct DrawManager { - enum class DrawMode : u32 { General = 0, Instance, InlineIndex, InstanceArray }; + enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; struct State { Maxwell3D::Regs::PrimitiveTopology topology{}; DrawMode draw_mode{}; @@ -3071,7 +3071,6 @@ public: void ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument); void Clear(Maxwell3D& maxwell3d, u32 layer_count); void DrawDeferred(Maxwell3D& maxwell3d); - void FlushInstanceArray(Maxwell3D& maxwell3d); void DrawArray(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances); void DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, bool subsequent); void DrawIndex(Maxwell3D& maxwell3d, Maxwell3D::Regs::PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances); @@ -3087,7 +3086,6 @@ public: void ProcessDraw(Maxwell3D& maxwell3d, bool draw_indexed, u32 instance_count); void ProcessDrawIndirect(Maxwell3D& maxwell3d); State draw_state{}; - Maxwell3D::Regs::PrimitiveTopology instance_topology{}; DrawTextureState draw_texture_state{}; IndirectParams indirect_state{}; }; diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 38b5a3e785..0b05d31718 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -4,8 +4,6 @@ // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include - #include "common/assert.h" #include "common/logging.h" #include "common/settings.h" @@ -73,7 +71,7 @@ void Puller::ProcessFenceActionMethod(DmaPusher& dma_pusher) { dma_pusher.rasterizer->ReleaseFences(); break; case Puller::FenceOperation::Increment: - dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id, 1); + dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); break; default: UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); @@ -280,9 +278,6 @@ void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, ASSERT(subchannel < bound_engines.size()); if (ExecuteMethodOnEngine(dma_pusher, method)) { CallEngineMultiMethod(dma_pusher, method, subchannel, base_start, amount, methods_pending); - } else if (IsIncrementRun(method, base_start, amount)) { - regs.reg_array[method] = base_start[0]; - dma_pusher.rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id, amount); } else { for (u32 i = 0; i < amount; i++) { CallPullerMethod(dma_pusher, MethodCall{ @@ -295,16 +290,6 @@ void Puller::CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, } } -bool Puller::IsIncrementRun(u32 method, const u32* base_start, u32 amount) const { - if (BufferMethods(method) != BufferMethods::SyncpointOperation || amount < 2) { - return false; - } - const FenceAction action{.raw = base_start[0]}; - return action.op == FenceOperation::Increment && - std::all_of(base_start, base_start + amount, - [first = base_start[0]](u32 argument) { return argument == first; }); -} - /// Determines where the method should be executed. [[nodiscard]] bool Puller::ExecuteMethodOnEngine(DmaPusher& dma_pusher, u32 method) { const auto buffer_method = BufferMethods(method); diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h index c1a1d86e2a..e8994f5640 100644 --- a/src/video_core/engines/puller.h +++ b/src/video_core/engines/puller.h @@ -72,7 +72,6 @@ public: void CallMethod(DmaPusher& dma_pusher, const MethodCall& method_call); void CallMultiMethod(DmaPusher& dma_pusher, u32 method, u32 subchannel, const u32* base_start, u32 amount, u32 methods_pending); - [[nodiscard]] bool IsIncrementRun(u32 method, const u32* base_start, u32 amount) const; void BindRasterizer(DmaPusher& dma_pusher, VideoCore::RasterizerInterface* rasterizer); void CallPullerMethod(DmaPusher& dma_pusher, const MethodCall& method_call); void CallEngineMethod(DmaPusher& dma_pusher, const MethodCall& method_call); diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp index a360ffb125..8bcc2f7a76 100644 --- a/src/video_core/engines/sw_blitter/blitter.cpp +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -95,8 +92,7 @@ void Bilinear(std::span input, std::span output, size_t src_widt const auto read_src = [&](f32 in_x, f32 in_y) { const size_t read_from = - ((std::min)(static_cast(in_y), src_height - 1) * src_width + - (std::min)(static_cast(in_x), src_width - 1)) * + ((static_cast(in_x) * src_width + static_cast(in_y)) >> 32) * ir_components; return std::span(&input[read_from], ir_components); }; diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index b7d53ad583..776c01e3fa 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -72,7 +72,7 @@ public: } void SignalFence(std::function&& func) { - const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict(); + const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate(); const bool should_flush = ShouldFlush(); if constexpr (!can_async_check) { TryReleasePendingFences(); @@ -101,15 +101,9 @@ public: rasterizer.InvalidateGPUCache(); } - void SignalSyncPoint(u32 value, u32 count) { - for (u32 i = 0; i < count; ++i) { - syncpoint_manager.IncrementGuest(value); - } - std::function func([this, value, count] { - for (u32 i = 0; i < count; ++i) { - syncpoint_manager.IncrementHost(value); - } - }); + void SignalSyncPoint(u32 value) { + syncpoint_manager.IncrementGuest(value); + std::function func([this, value] { syncpoint_manager.IncrementHost(value); }); SignalFence(std::move(func)); } diff --git a/src/video_core/frame_gen/lossless_dll.cpp b/src/video_core/frame_gen/lossless_dll.cpp index 32c36f195e..6be1e5530f 100644 --- a/src/video_core/frame_gen/lossless_dll.cpp +++ b/src/video_core/frame_gen/lossless_dll.cpp @@ -157,7 +157,7 @@ private: [[nodiscard]] std::optional RvaToFileOffset(std::span sections, u32 rva) { for (const Section& section : sections) { - const u32 span = (std::max)(section.virtual_size, section.raw_size); + const u32 span = std::max(section.virtual_size, section.raw_size); if (span == 0 || rva < section.virtual_address) { continue; } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index eb834308e0..1621b24125 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -152,10 +152,6 @@ struct GPU::Impl { /// Tick pending requests within the GPU. void TickWork() { - if (last_sync_fence.load(std::memory_order_relaxed) == - current_sync_fence.load(std::memory_order_relaxed)) { - return; - } std::unique_lock lck{sync_request_mutex}; while (!sync_requests.empty()) { auto request = std::move(sync_requests.front()); @@ -230,7 +226,19 @@ struct GPU::Impl { } /// Notify rasterizer that any caches of the specified region should be invalidated - void InvalidateRegion(DAddr addr, u64 size) { + void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) { + if (preserve_gpu_writes) { + VideoCore::RasterizerInterface* rasterizer = renderer->ReadRasterizer(); + if (!rasterizer->TryCacheInvalidation(addr, size)) { + const u64 fence = RequestSyncOperation([rasterizer, addr, size] { + rasterizer->FlushRegion(addr, size, VideoCommon::CacheType::BufferCache); + rasterizer->OnCacheInvalidation(addr, size); + }); + gpu_thread.TickGPU(is_async); + WaitForSyncOperation(fence); + } + return; + } gpu_thread.InvalidateRegion(addr, size); } @@ -332,7 +340,7 @@ struct GPU::Impl { std::list> sync_requests; std::atomic current_sync_fence{}; - std::atomic last_sync_fence{}; + u64 last_sync_fence{}; std::mutex sync_request_mutex; std::condition_variable sync_request_cv; @@ -522,8 +530,8 @@ void GPU::FlushRegion(DAddr addr, u64 size) { impl->FlushRegion(addr, size); } -void GPU::InvalidateRegion(DAddr addr, u64 size) { - impl->InvalidateRegion(addr, size); +void GPU::InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) { + impl->InvalidateRegion(addr, size, preserve_gpu_writes); } bool GPU::OnCPUWrite(DAddr addr, u64 size) { diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 75956ea199..974d214e10 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -248,7 +248,7 @@ public: void FlushRegion(DAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be invalidated - void InvalidateRegion(DAddr addr, u64 size); + void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes = false); /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is /// sensible, false otherwise, addr and size must be a valid combination diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 468bdc19ec..c4e7ac3f6f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -22,6 +22,7 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp + ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag @@ -78,7 +79,6 @@ set(SHADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_easu_fp32.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp16.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_fidelityfx_fsr_rcas_fp32.frag - ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_indirect_quads.comp ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.frag ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present.vert ${CMAKE_CURRENT_SOURCE_DIR}/vulkan_present_scaleforce_fp16.frag diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index af6fac0e84..e059c683ff 100644 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -55,7 +55,6 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT; const uint BYTES_PER_BLOCK_LOG2 = 4; -// DO NOT CHANGE - code depends on the value of these! const uint JUST_BITS = 0u; const uint QUINT = 1u; const uint TRIT = 2u; @@ -63,45 +62,27 @@ const uint TRIT = 2u; // ASTC Encodings data, sorted in ascending order based on their BitLength value // (see GetBitLength() function) const uint encoding_values[22] = uint[]( - (JUST_BITS), - (JUST_BITS | (1u << 8u)), - (TRIT), - (JUST_BITS | (2u << 8u)), - (QUINT), - (TRIT | (1u << 8u)), - (JUST_BITS | (3u << 8u)), - (QUINT | (1u << 8u)), - (TRIT | (2u << 8u)), - (JUST_BITS | (4u << 8u)), - (QUINT | (2u << 8u)), - (TRIT | (3u << 8u)), - (JUST_BITS | (5u << 8u)), - (QUINT | (3u << 8u)), - (TRIT | (4u << 8u)), - (JUST_BITS | (6u << 8u)), - (QUINT | (4u << 8u)), - (TRIT | (5u << 8u)), - (JUST_BITS | (7u << 8u)), - (QUINT | (5u << 8u)), - (TRIT | (6u << 8u)), - (JUST_BITS | (8u << 8u)) -); + (JUST_BITS), (JUST_BITS | (1u << 8u)), (TRIT), (JUST_BITS | (2u << 8u)), + (QUINT), (TRIT | (1u << 8u)), (JUST_BITS | (3u << 8u)), (QUINT | (1u << 8u)), + (TRIT | (2u << 8u)), (JUST_BITS | (4u << 8u)), (QUINT | (2u << 8u)), (TRIT | (3u << 8u)), + (JUST_BITS | (5u << 8u)), (QUINT | (3u << 8u)), (TRIT | (4u << 8u)), (JUST_BITS | (6u << 8u)), + (QUINT | (4u << 8u)), (TRIT | (5u << 8u)), (JUST_BITS | (7u << 8u)), (QUINT | (5u << 8u)), + (TRIT | (6u << 8u)), (JUST_BITS | (8u << 8u))); // Input ASTC texture globals -uint total_bitsread = 0; +int total_bitsread = 0; +uvec4 local_buff; + // Color data globals uvec4 color_endpoint_data; -uint color_bitsread = 0; +int color_bitsread = 0; -// Global "vector" to be pushed into when decoding -// At most will require BLOCK_WIDTH x BLOCK_HEIGHT in single plane mode -// At most will require BLOCK_WIDTH x BLOCK_HEIGHT x 2 in dual plane mode -// So the maximum would be 144 (12 x 12) elements, x 2 for two planes #define MAX_WEIGHT_VALUES 64 -uint result_vector[MAX_WEIGHT_VALUES + 1]; +uint result_vector[MAX_WEIGHT_VALUES]; int result_index = 0; uint result_vector_max_index; +bool result_limit_reached = false; // EncodingData helpers uint Encoding(EncodingData val) { @@ -137,10 +118,12 @@ EncodingData CreateEncodingData(uint encoding, uint num_bits, uint bit_val, uint void ResultEmplaceBack(EncodingData val) { - // A = result_index, B = result_vector_max_index - // A >= B -> A - B >= 0 - // A < B -> A - B < 0 - result_vector[min(uint(MAX_WEIGHT_VALUES), result_index)] = val.data; + if (result_index >= result_vector_max_index) { + // Alert callers to avoid decoding more than needed by this phase + result_limit_reached = true; + return; + } + result_vector[result_index] = val.data; ++result_index; } @@ -156,31 +139,49 @@ uint ReplicateBitTo9(uint value) { return value * 511; } -const uint mod6_table = 0 - | ((6 % 1) << (2 * 1)) | ((6 % 2) << (2 * 2)) - | ((6 % 3) << (2 * 3)) | ((6 % 4) << (2 * 4)) - | ((6 % 5) << (2 * 5)) | ((6 % 6) << (2 * 6)); -const uint mod8_table = 0 - | ((8 % 1) << (2 * 1)) | ((8 % 2) << (2 * 2)) - | ((8 % 3) << (2 * 3)) | ((8 % 4) << (2 * 4)) - | ((8 % 5) << (2 * 5)) | ((8 % 6) << (2 * 6)) - | ((8 % 7) << (2 * 7)) | ((8 % 8) << (2 * 8)); -// Assumes num_bits < to_bit, num_bits and to_bit != 0 -uint ReplicateBits(uint value, uint num_bits, uint to_bit, uint table) { - const uint repl = value & ((1 << num_bits) - 1); - const uint shift = (table >> (num_bits << 1)) & 3; - uint v = repl; - v |= v << (num_bits << 0); // [ xxxx xxrr ] - v |= v << (num_bits << 1); // [ xxxx rrrr ] - v |= v << (num_bits << 2); // [ rrrr rrrr ] - v = (v << shift) | repl >> (num_bits - shift); - return v & ((1 << to_bit) - 1); +uint ReplicateBits(uint value, uint num_bits, uint to_bit) { + if (value == 0 || num_bits == 0) { + return 0; + } + if (num_bits >= to_bit) { + return value; + } + const uint v = value & uint((1 << num_bits) - 1); + uint res = v; + uint reslen = num_bits; + while (reslen < to_bit) { + const uint num_dst_bits_to_shift_up = min(num_bits, to_bit - reslen); + const uint num_src_bits_to_shift_down = num_bits - num_dst_bits_to_shift_up; + + res <<= num_dst_bits_to_shift_up; + res |= (v >> num_src_bits_to_shift_down); + reslen += num_bits; + } + return res; } + uint FastReplicateTo8(uint value, uint num_bits) { - return ReplicateBits(value, num_bits, 8, mod8_table); + return ReplicateBits(value, num_bits, 8); } + uint FastReplicateTo6(uint value, uint num_bits) { - return ReplicateBits(value, num_bits, 6, mod6_table); + return ReplicateBits(value, num_bits, 6); +} + +uint Div3Floor(uint v) { + return (v * 0x5556) >> 16; +} + +uint Div3Ceil(uint v) { + return Div3Floor(v + 2); +} + +uint Div5Floor(uint v) { + return (v * 0x3334) >> 16; +} + +uint Div5Ceil(uint v) { + return Div5Floor(v + 4); } uint Hash52(uint p) { @@ -197,85 +198,139 @@ uint Hash52(uint p) { return p; } -uint Select2DPartition(uint seed, uvec2 pos, uint partition_count) { - pos <<= uint(block_dims.y * block_dims.x < 32); +uint Select2DPartition(uint seed, uint x, uint y, uint partition_count) { + if ((block_dims.y * block_dims.x) < 32) { + x <<= 1; + y <<= 1; + } + seed += (partition_count - 1) * 1024; + const uint rnum = Hash52(uint(seed)); - uvec2 shift = uvec2( - (seed & 2) > 0 ? 4 : 5, - (partition_count == 3) ? 6 : 5 - ); - shift.xy = (seed & 1) > 0 ? shift.xy : shift.yx; - uvec4 rseed[2] = uvec4[]( - (uvec4(rnum) >> uvec4(0, 4, 8, 12)) & 0xf, - (uvec4(rnum) >> uvec4(16, 20, 24, 28)) & 0xf - ); - rseed[0] = (rseed[0] * rseed[0]) >> shift.xyxy; - rseed[1] = (rseed[1] * rseed[1]) >> shift.xyxy; - const uvec4 rnum_vec = uvec4(rnum) >> uvec4(14, 10, 6, 2); - const uvec4 result_mask = ((uvec4(0, 1, 2, 3) - partition_count) >> 8) & 0x3f; - uvec4 result = uvec4( - rseed[0].xz * pos.xx + rseed[0].yw * pos.yy + rnum_vec.xy, - rseed[1].xz * pos.xx + rseed[1].yw * pos.yy + rnum_vec.zw - ) & result_mask; - if (result.x >= result.y && result.x >= result.z && result.x >= result.w) { + uint seed1 = uint(rnum & 0xF); + uint seed2 = uint((rnum >> 4) & 0xF); + uint seed3 = uint((rnum >> 8) & 0xF); + uint seed4 = uint((rnum >> 12) & 0xF); + uint seed5 = uint((rnum >> 16) & 0xF); + uint seed6 = uint((rnum >> 20) & 0xF); + uint seed7 = uint((rnum >> 24) & 0xF); + uint seed8 = uint((rnum >> 28) & 0xF); + + seed1 = (seed1 * seed1); + seed2 = (seed2 * seed2); + seed3 = (seed3 * seed3); + seed4 = (seed4 * seed4); + seed5 = (seed5 * seed5); + seed6 = (seed6 * seed6); + seed7 = (seed7 * seed7); + seed8 = (seed8 * seed8); + + uint sh1, sh2; + if ((seed & 1) > 0) { + sh1 = (seed & 2) > 0 ? 4 : 5; + sh2 = (partition_count == 3) ? 6 : 5; + } else { + sh1 = (partition_count == 3) ? 6 : 5; + sh2 = (seed & 2) > 0 ? 4 : 5; + } + seed1 >>= sh1; + seed2 >>= sh2; + seed3 >>= sh1; + seed4 >>= sh2; + seed5 >>= sh1; + seed6 >>= sh2; + seed7 >>= sh1; + seed8 >>= sh2; + + uint a = seed1 * x + seed2 * y + (rnum >> 14); + uint b = seed3 * x + seed4 * y + (rnum >> 10); + uint c = seed5 * x + seed6 * y + (rnum >> 6); + uint d = seed7 * x + seed8 * y + (rnum >> 2); + + a &= 0x3F; + b &= 0x3F; + c &= 0x3F; + d &= 0x3F; + + if (partition_count < 4) { + d = 0; + } + if (partition_count < 3) { + c = 0; + } + + if (a >= b && a >= c && a >= d) { return 0; - } else if (result.y >= result.z && result.y >= result.w) { + } else if (b >= c && b >= d) { return 1; - } else if (result.z >= result.w) { + } else if (c >= d) { return 2; } else { return 3; } } -uint ExtractBits(uvec4 payload, uint offset, uint bits) { - if (bits == 0 || bits > 32 || offset >= 128) +uint ExtractBits(uvec4 payload, int offset, int bits) { + if (bits <= 0) { return 0; - const uint avail_bits = min(bits, 128 - offset); - const uint last_offset = offset + avail_bits - 1; - const uint shifted_offset = offset >> 5; - if ((last_offset >> 5) == shifted_offset) - return bitfieldExtract(payload[shifted_offset], int(offset & 31), int(avail_bits)); - const uint first_bits = 32 - (offset & 31); - const uvec4 next = uvec4(payload.yzw, 0); - return bitfieldExtract(payload[shifted_offset], int(offset & 31), int(first_bits)) - | (bitfieldExtract(next[shifted_offset], 0, int(avail_bits - first_bits)) << first_bits); + } + if (bits > 32) { + return 0; + } + const int last_offset = offset + bits - 1; + const int shifted_offset = offset >> 5; + if ((last_offset >> 5) == shifted_offset) { + return bitfieldExtract(payload[shifted_offset], offset & 31, bits); + } + const int first_bits = 32 - (offset & 31); + const int result_first = int(bitfieldExtract(payload[shifted_offset], offset & 31, first_bits)); + const int result_second = int(bitfieldExtract(payload[shifted_offset + 1], 0, bits - first_bits)); + return result_first | (result_second << first_bits); } -uint StreamBits(uvec4 local_buff, uint num_bits) { - const uint ret = ExtractBits(local_buff, total_bitsread, num_bits); - total_bitsread += num_bits; +uint StreamBits(uint num_bits) { + const int int_bits = int(num_bits); + const uint ret = ExtractBits(local_buff, total_bitsread, int_bits); + total_bitsread += int_bits; return ret; } void SkipBits(uint num_bits) { - total_bitsread += num_bits; + const int int_bits = int(num_bits); + total_bitsread += int_bits; } uint StreamColorBits(uint num_bits) { - const uint ret = ExtractBits(color_endpoint_data, color_bitsread, num_bits); - color_bitsread += num_bits; + const int int_bits = int(num_bits); + const uint ret = ExtractBits(color_endpoint_data, color_bitsread, int_bits); + color_bitsread += int_bits; return ret; } +EncodingData GetEncodingFromVector(uint index) { + const uint data = result_vector[index]; + return EncodingData(data); +} + // Returns the number of bits required to encode n_vals values. uint GetBitLength(uint n_vals, uint encoding_index) { - // uint Div3Floor(uint v) { return (v * 0x5556) >> 16; } - // uint Div3Ceil(uint v) { return Div3Floor(v + 2); } - // uint Div5Floor(uint v) { return (v * 0x3334) >> 16; } - // uint Div5Ceil(uint v) { return Div5Floor(v + 4); } const EncodingData encoding_value = EncodingData(encoding_values[encoding_index]); const uint encoding = Encoding(encoding_value); - const uint num_bits = NumBits(encoding_value); - const uvec3 div_constant = uvec3(0, 0x5556, 0x3334); - return num_bits * n_vals - + ((((n_vals * ((0x870 >> (encoding << 2)) & 0xf)) + ((0x420 >> (encoding << 2)) & 0xf)) - * div_constant[encoding]) >> 16); + uint total_bits = NumBits(encoding_value) * n_vals; + if (encoding == TRIT) { + total_bits += Div5Ceil(n_vals * 8); + } else if (encoding == QUINT) { + total_bits += Div3Ceil(n_vals * 7); + } + return total_bits; } uint GetNumWeightValues(uvec2 size, bool dual_plane) { - return (size.x * size.y) << uint(dual_plane); + uint n_vals = size.x * size.y; + if (dual_plane) { + n_vals *= 2; + } + return n_vals; } uint GetPackedBitSize(uvec2 size, bool dual_plane, uint max_weight) { @@ -304,16 +359,13 @@ void DecodeQuintBlock(uint num_bits) { if (BitsOp(qQ.w, 1, 2) == 3 && BitsOp(qQ.w, 5, 6) == 0) { qQ.x = 4; qQ.y = 4; - qQ.z = (BitsBracket(qQ.w, 0) << 2) - | ((BitsBracket(qQ.w, 4) & ~BitsBracket(qQ.w, 0)) << 1) - | (BitsBracket(qQ.w, 3) & ~BitsBracket(qQ.w, 0)); + qQ.z = (BitsBracket(qQ.w, 0) << 2) | ((BitsBracket(qQ.w, 4) & ~BitsBracket(qQ.w, 0)) << 1) | + (BitsBracket(qQ.w, 3) & ~BitsBracket(qQ.w, 0)); } else { uint C = 0; if (BitsOp(qQ.w, 1, 2) == 3) { qQ.z = 4; - C = (BitsOp(qQ.w, 3, 4) << 3) - | ((~BitsOp(qQ.w, 5, 6) & 3) << 1) - | BitsBracket(qQ.w, 0); + C = (BitsOp(qQ.w, 3, 4) << 3) | ((~BitsOp(qQ.w, 5, 6) & 3) << 1) | BitsBracket(qQ.w, 0); } else { qQ.z = BitsOp(qQ.w, 5, 6); C = BitsOp(qQ.w, 0, 4); @@ -326,37 +378,39 @@ void DecodeQuintBlock(uint num_bits) { qQ.x = BitsOp(C, 0, 2); } } - for (uint i = 0; i < 3; i++) - ResultEmplaceBack(CreateEncodingData(QUINT, num_bits, m[i], qQ[i])); + for (uint i = 0; i < 3; i++) { + const EncodingData val = CreateEncodingData(QUINT, num_bits, m[i], qQ[i]); + ResultEmplaceBack(val); + } } void DecodeTritBlock(uint num_bits) { uvec4 m; uvec4 t; - uvec3 tm5t5; + uvec3 Tm5t5; m[0] = StreamColorBits(num_bits); - tm5t5.x = StreamColorBits(2); + Tm5t5.x = StreamColorBits(2); m[1] = StreamColorBits(num_bits); - tm5t5.x |= StreamColorBits(2) << 2; + Tm5t5.x |= StreamColorBits(2) << 2; m[2] = StreamColorBits(num_bits); - tm5t5.x |= StreamColorBits(1) << 4; + Tm5t5.x |= StreamColorBits(1) << 4; m[3] = StreamColorBits(num_bits); - tm5t5.x |= StreamColorBits(2) << 5; - tm5t5.y = StreamColorBits(num_bits); - tm5t5.x |= StreamColorBits(1) << 7; + Tm5t5.x |= StreamColorBits(2) << 5; + Tm5t5.y = StreamColorBits(num_bits); + Tm5t5.x |= StreamColorBits(1) << 7; uint C = 0; - if (BitsOp(tm5t5.x, 2, 4) == 7) { - C = (BitsOp(tm5t5.x, 5, 7) << 2) | BitsOp(tm5t5.x, 0, 1); - tm5t5.z = 2; + if (BitsOp(Tm5t5.x, 2, 4) == 7) { + C = (BitsOp(Tm5t5.x, 5, 7) << 2) | BitsOp(Tm5t5.x, 0, 1); + Tm5t5.z = 2; t[3] = 2; } else { - C = BitsOp(tm5t5.x, 0, 4); - if (BitsOp(tm5t5.x, 5, 6) == 3) { - tm5t5.z = 2; - t[3] = BitsBracket(tm5t5.x, 7); + C = BitsOp(Tm5t5.x, 0, 4); + if (BitsOp(Tm5t5.x, 5, 6) == 3) { + Tm5t5.z = 2; + t[3] = BitsBracket(Tm5t5.x, 7); } else { - tm5t5.z = BitsBracket(tm5t5.x, 7); - t[3] = BitsOp(tm5t5.x, 5, 6); + Tm5t5.z = BitsBracket(Tm5t5.x, 7); + t[3] = BitsOp(Tm5t5.x, 5, 6); } } if (BitsOp(C, 0, 1) == 3) { @@ -372,147 +426,156 @@ void DecodeTritBlock(uint num_bits) { t[1] = BitsOp(C, 2, 3); t[0] = (BitsBracket(C, 1) << 1) | (BitsBracket(C, 0) & ~BitsBracket(C, 1)); } - for (uint i = 0; i < 4; i++) - ResultEmplaceBack(CreateEncodingData(TRIT, num_bits, m[i], t[i])); - ResultEmplaceBack(CreateEncodingData(TRIT, num_bits, tm5t5.y, tm5t5.z)); + for (uint i = 0; i < 4; i++) { + const EncodingData val = CreateEncodingData(TRIT, num_bits, m[i], t[i]); + ResultEmplaceBack(val); + } + const EncodingData val = CreateEncodingData(TRIT, num_bits, Tm5t5.y, Tm5t5.z); + ResultEmplaceBack(val); } void DecodeIntegerSequence(uint max_range, uint num_values) { EncodingData val = EncodingData(encoding_values[max_range]); const uint encoding = Encoding(val); const uint num_bits = NumBits(val); - for (uint i = 0; i < num_values && result_index < result_vector_max_index; ) { + uint vals_decoded = 0; + while (vals_decoded < num_values && !result_limit_reached) { switch (encoding) { case QUINT: DecodeQuintBlock(num_bits); - i += 3; + vals_decoded += 3; break; case TRIT: DecodeTritBlock(num_bits); - i += 5; + vals_decoded += 5; break; case JUST_BITS: BitValue(val, StreamColorBits(num_bits)); ResultEmplaceBack(val); - i++; + vals_decoded++; break; } } } -uint DecodeSingleColorValue(EncodingData val) { - const uint encoding = Encoding(val); - const uint bitlen = NumBits(val); - const uint bitval = BitValue(val); - uint B = 0, C = 0, D = 0; - uint A = ReplicateBitTo9((bitval & 1)); - switch (encoding) { - case JUST_BITS: - break; - case TRIT: { - D = QuintTritValue(val); - switch (bitlen) { - case 1: - C = 204; - break; - case 2: { - C = 93; - const uint b = (bitval >> 1) & 1; - B = (b << 8) | (b << 4) | (b << 2) | (b << 1); - break; - } - case 3: { - C = 44; - const uint cb = (bitval >> 1) & 3; - B = (cb << 7) | (cb << 2) | cb; - break; - } - case 4: { - C = 22; - const uint dcb = (bitval >> 1) & 7; - B = (dcb << 6) | dcb; - break; - } - case 5: { - C = 11; - const uint edcb = (bitval >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 2); - break; - } - case 6: { - C = 5; - const uint fedcb = (bitval >> 1) & 0x1F; - B = (fedcb << 4) | (fedcb >> 4); - break; - } - } - break; - } - case QUINT: { - D = QuintTritValue(val); - switch (bitlen) { - case 1: - C = 113; - break; - case 2: { - C = 54; - const uint b = (bitval >> 1) & 1; - B = (b << 8) | (b << 3) | (b << 2); - break; - } - case 3: { - C = 26; - const uint cb = (bitval >> 1) & 3; - B = (cb << 7) | (cb << 1) | (cb >> 1); - break; - } - case 4: { - C = 13; - const uint dcb = (bitval >> 1) & 7; - B = (dcb << 6) | (dcb >> 1); - break; - } - case 5: { - C = 6; - const uint edcb = (bitval >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 3); - break; - } - } - break; - } - } - uint unq = D * C + B; - unq = unq ^ A; - unq = (A & 0x80) | (unq >> 2); - return encoding == JUST_BITS ? FastReplicateTo8(bitval, bitlen) : unq; -} - void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits, out uint color_values[32]) { - // TODO: modes[] zero on invalid, so less ops - const uvec4 num_values_tmp = (((modes >> 2) + 1) << 1) & ((uvec4(0, 1, 2, 3) - num_partitions) >> 8); - uint num_values = num_values_tmp.x + num_values_tmp.y + num_values_tmp.z + num_values_tmp.w; + uint num_values = 0; + for (uint i = 0; i < num_partitions; i++) { + num_values += ((modes[i] >> 2) + 1) << 1; + } // Find the largest encoding that's within color_data_bits // TODO(ameerj): profile with binary search - uint range = 0; + int range = 0; while (++range < encoding_values.length()) { - const uint bit_length = GetBitLength(num_values, int(range)); + const uint bit_length = GetBitLength(num_values, range); if (bit_length > color_data_bits) { break; } } - const uint upper_bound = num_values; DecodeIntegerSequence(range - 1, num_values); - for (int i = 0; i < upper_bound; ++i) { - color_values[i + 1] = DecodeSingleColorValue(EncodingData(result_vector[i])); + uint out_index = 0; + for (int itr = 0; itr < result_index; ++itr) { + if (out_index >= num_values) { + break; + } + const EncodingData val = GetEncodingFromVector(itr); + const uint encoding = Encoding(val); + const uint bitlen = NumBits(val); + const uint bitval = BitValue(val); + uint A = 0, B = 0, C = 0, D = 0; + A = ReplicateBitTo9((bitval & 1)); + switch (encoding) { + case JUST_BITS: + color_values[out_index++] = FastReplicateTo8(bitval, bitlen); + break; + case TRIT: { + D = QuintTritValue(val); + switch (bitlen) { + case 1: + C = 204; + break; + case 2: { + C = 93; + const uint b = (bitval >> 1) & 1; + B = (b << 8) | (b << 4) | (b << 2) | (b << 1); + break; + } + case 3: { + C = 44; + const uint cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 2) | cb; + break; + } + case 4: { + C = 22; + const uint dcb = (bitval >> 1) & 7; + B = (dcb << 6) | dcb; + break; + } + case 5: { + C = 11; + const uint edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 2); + break; + } + case 6: { + C = 5; + const uint fedcb = (bitval >> 1) & 0x1F; + B = (fedcb << 4) | (fedcb >> 4); + break; + } + } + break; + } + case QUINT: { + D = QuintTritValue(val); + switch (bitlen) { + case 1: + C = 113; + break; + case 2: { + C = 54; + const uint b = (bitval >> 1) & 1; + B = (b << 8) | (b << 3) | (b << 2); + break; + } + case 3: { + C = 26; + const uint cb = (bitval >> 1) & 3; + B = (cb << 7) | (cb << 1) | (cb >> 1); + break; + } + case 4: { + C = 13; + const uint dcb = (bitval >> 1) & 7; + B = (dcb << 6) | (dcb >> 1); + break; + } + case 5: { + C = 6; + const uint edcb = (bitval >> 1) & 0xF; + B = (edcb << 5) | (edcb >> 3); + break; + } + } + break; + } + } + if (encoding != JUST_BITS) { + uint T = (D * C) + B; + T ^= A; + T = (A & 0x80) | (T >> 2); + color_values[out_index++] = T; + } } } ivec2 BitTransferSigned(int a, int b) { - ivec2 transferred = ivec2( - (a >> 1) & 0x3F, - (b >> 1) | (a & 0x80) - ); + ivec2 transferred; + transferred.y = b >> 1; + transferred.y |= a & 0x80; + transferred.x = a >> 1; + transferred.x &= 0x3F; if ((transferred.x & 0x20) > 0) { transferred.x -= 0x40; } @@ -527,17 +590,169 @@ ivec4 BlueContract(int a, int r, int g, int b) { return ivec4(a, (r + b) >> 1, (g + b) >> 1, b); } +bool IsHDRColorEndpointMode(uint cem) { + return cem == 2u || cem == 3u || cem == 7u || cem == 11u || cem == 14u || cem == 15u; +} + +int SignExtend(int value, uint nbits) { + int sign_bit = 1 << (nbits - 1u); + return (value ^ sign_bit) - sign_bit; +} + +void DecodeHDREndpointMode7(uint v0, uint v1, uint v2, uint v3, out ivec3 e0, out ivec3 e1) { + uint modeval = ((v0 & 0xC0u) >> 6u) | ((v1 & 0x80u) >> 5u) | ((v2 & 0x80u) >> 4u); + + uint majcomp; + uint mode; + if ((modeval & 0xCu) != 0xCu) { + majcomp = modeval >> 2u; + mode = modeval & 3u; + } else if (modeval != 0xFu) { + majcomp = modeval & 3u; + mode = 4u; + } else { + majcomp = 0u; + mode = 5u; + } + + int red = int(v0 & 0x3Fu); + int green = int(v1 & 0x1Fu); + int blue = int(v2 & 0x1Fu); + int scale = int(v3 & 0x1Fu); + + uint x0 = (v1 >> 6u) & 1u; + uint x1 = (v1 >> 5u) & 1u; + uint x2 = (v2 >> 6u) & 1u; + uint x3 = (v2 >> 5u) & 1u; + uint x4 = (v3 >> 7u) & 1u; + uint x5 = (v3 >> 6u) & 1u; + uint x6 = (v3 >> 5u) & 1u; + + uint ohm = 1u << mode; + if ((ohm & 0x30u) != 0u) green |= int(x0 << 6u); + if ((ohm & 0x3Au) != 0u) green |= int(x1 << 5u); + if ((ohm & 0x30u) != 0u) blue |= int(x2 << 6u); + if ((ohm & 0x3Au) != 0u) blue |= int(x3 << 5u); + if ((ohm & 0x3Du) != 0u) scale |= int(x6 << 5u); + if ((ohm & 0x2Du) != 0u) scale |= int(x5 << 6u); + if ((ohm & 0x04u) != 0u) scale |= int(x4 << 7u); + if ((ohm & 0x3Bu) != 0u) red |= int(x4 << 6u); + if ((ohm & 0x04u) != 0u) red |= int(x3 << 6u); + if ((ohm & 0x10u) != 0u) red |= int(x5 << 7u); + if ((ohm & 0x0Fu) != 0u) red |= int(x2 << 7u); + if ((ohm & 0x05u) != 0u) red |= int(x1 << 8u); + if ((ohm & 0x0Au) != 0u) red |= int(x0 << 8u); + if ((ohm & 0x05u) != 0u) red |= int(x0 << 9u); + if ((ohm & 0x02u) != 0u) red |= int(x6 << 9u); + if ((ohm & 0x01u) != 0u) red |= int(x3 << 10u); + if ((ohm & 0x02u) != 0u) red |= int(x5 << 10u); + + int shamts[6] = int[](1, 1, 2, 3, 4, 5); + int shamt = shamts[mode]; + red <<= shamt; + green <<= shamt; + blue <<= shamt; + scale <<= shamt; + + if (mode != 5u) { + green = red - green; + blue = red - blue; + } + + if (majcomp == 1u) { + int t = red; red = green; green = t; + } + if (majcomp == 2u) { + int t = red; red = blue; blue = t; + } + + e1 = ivec3(clamp(red, 0, 0xFFF), clamp(green, 0, 0xFFF), clamp(blue, 0, 0xFFF)); + e0 = ivec3(clamp(red - scale, 0, 0xFFF), clamp(green - scale, 0, 0xFFF), + clamp(blue - scale, 0, 0xFFF)); +} + +void DecodeHDREndpointMode11(uint v0, uint v1, uint v2, uint v3, uint v4, uint v5, out ivec3 e0, + out ivec3 e1) { + uint majcomp = ((v4 & 0x80u) >> 7u) | ((v5 & 0x80u) >> 6u); + if (majcomp == 3u) { + e0 = ivec3(int(v0 << 4u), int(v2 << 4u), int((v4 & 0x7Fu) << 5u)); + e1 = ivec3(int(v1 << 4u), int(v3 << 4u), int((v5 & 0x7Fu) << 5u)); + return; + } + + uint mode = ((v1 & 0x80u) >> 7u) | ((v2 & 0x80u) >> 6u) | ((v3 & 0x80u) >> 5u); + int va = int(v0 | ((v1 & 0x40u) << 2u)); + int vb0 = int(v2 & 0x3Fu); + int vb1 = int(v3 & 0x3Fu); + int vc = int(v1 & 0x3Fu); + int vd0 = int(v4 & 0x7Fu); + int vd1 = int(v5 & 0x7Fu); + + int dbitstab[8] = int[](7, 6, 7, 6, 5, 6, 5, 6); + vd0 = SignExtend(vd0, uint(dbitstab[mode])); + vd1 = SignExtend(vd1, uint(dbitstab[mode])); + + uint x0 = (v2 >> 6u) & 1u; + uint x1 = (v3 >> 6u) & 1u; + uint x2 = (v4 >> 6u) & 1u; + uint x3 = (v5 >> 6u) & 1u; + uint x4 = (v4 >> 5u) & 1u; + uint x5 = (v5 >> 5u) & 1u; + + uint ohm = 1u << mode; + if ((ohm & 0xA4u) != 0u) va |= int(x0 << 9u); + if ((ohm & 0x08u) != 0u) va |= int(x2 << 9u); + if ((ohm & 0x50u) != 0u) va |= int(x4 << 9u); + if ((ohm & 0x50u) != 0u) va |= int(x5 << 10u); + if ((ohm & 0xA0u) != 0u) va |= int(x1 << 10u); + if ((ohm & 0xC0u) != 0u) va |= int(x2 << 11u); + if ((ohm & 0x04u) != 0u) vc |= int(x1 << 6u); + if ((ohm & 0xE8u) != 0u) vc |= int(x3 << 6u); + if ((ohm & 0x20u) != 0u) vc |= int(x2 << 7u); + if ((ohm & 0x5Bu) != 0u) vb0 |= int(x0 << 6u); + if ((ohm & 0x5Bu) != 0u) vb1 |= int(x1 << 6u); + if ((ohm & 0x12u) != 0u) vb0 |= int(x2 << 7u); + if ((ohm & 0x12u) != 0u) vb1 |= int(x3 << 7u); + + int shamt = (int(mode) >> 1) ^ 3; + va <<= shamt; + vb0 <<= shamt; + vb1 <<= shamt; + vc <<= shamt; + vd0 <<= shamt; + vd1 <<= shamt; + + int r1 = clamp(va, 0, 0xFFF); + int g1 = clamp(va - vb0, 0, 0xFFF); + int b1 = clamp(va - vb1, 0, 0xFFF); + int r0 = clamp(va - vc, 0, 0xFFF); + int g0 = clamp(va - vb0 - vc - vd0, 0, 0xFFF); + int b0 = clamp(va - vb1 - vc - vd1, 0, 0xFFF); + + if (majcomp == 1u) { + int t; + t = r0; r0 = g0; g0 = t; + t = r1; r1 = g1; g1 = t; + } else if (majcomp == 2u) { + int t; + t = r0; r0 = b0; b0 = t; + t = r1; r1 = b1; b1 = t; + } + e0 = ivec3(r0, g0, b0); + e1 = ivec3(r1, g1, b1); +} + void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, uint color_values[32], inout uint colvals_index) { #define READ_UINT_VALUES(N) \ uvec4 V[2]; \ for (uint i = 0; i < N; i++) { \ - V[i >> 2][i & 3] = color_values[++colvals_index]; \ + V[i / 4][i % 4] = color_values[colvals_index++]; \ } #define READ_INT_VALUES(N) \ ivec4 V[2]; \ for (uint i = 0; i < N; i++) { \ - V[i >> 2][i & 3] = int(color_values[++colvals_index]); \ + V[i / 4][i % 4] = int(color_values[colvals_index++]); \ } switch (color_endpoint_mode) { @@ -563,8 +778,12 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui } case 5: { READ_INT_VALUES(4) - V[0].yx = BitTransferSigned(V[0].y, V[0].x); - V[0].wz = BitTransferSigned(V[0].w, V[0].z); + ivec2 transferred = BitTransferSigned(V[0].y, V[0].x); + V[0].y = transferred.x; + V[0].x = transferred.y; + transferred = BitTransferSigned(V[0].w, V[0].z); + V[0].w = transferred.x; + V[0].z = transferred.y; ep1 = ClampByte(ivec4(V[0].z, V[0].x, V[0].x, V[0].x)); ep2 = ClampByte(ivec4(V[0].z + V[0].w, V[0].x + V[0].y, V[0].x + V[0].y, V[0].x + V[0].y)); break; @@ -588,9 +807,15 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui } case 9: { READ_INT_VALUES(6) - V[0].yx = BitTransferSigned(V[0].y, V[0].x); - V[0].wz = BitTransferSigned(V[0].w, V[0].z); - V[1].yx = BitTransferSigned(V[1].y, V[1].x); + ivec2 transferred = BitTransferSigned(V[0].y, V[0].x); + V[0].y = transferred.x; + V[0].x = transferred.y; + transferred = BitTransferSigned(V[0].w, V[0].z); + V[0].w = transferred.x; + V[0].z = transferred.y; + transferred = BitTransferSigned(V[1].y, V[1].x); + V[1].y = transferred.x; + V[1].x = transferred.y; if ((V[0].y + V[0].w + V[1].y) >= 0) { ep1 = ClampByte(ivec4(0xFF, V[0].x, V[0].z, V[1].x)); ep2 = ClampByte(ivec4(0xFF, V[0].x + V[0].y, V[0].z + V[0].w, V[1].x + V[1].y)); @@ -619,10 +844,21 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui } case 13: { READ_INT_VALUES(8) - V[0].yx = BitTransferSigned(V[0].y, V[0].x); - V[0].wz = BitTransferSigned(V[0].w, V[0].z); - V[1].yx = BitTransferSigned(V[1].y, V[1].x); - V[1].wz = BitTransferSigned(V[1].w, V[1].z); + ivec2 transferred = BitTransferSigned(V[0].y, V[0].x); + V[0].y = transferred.x; + V[0].x = transferred.y; + transferred = BitTransferSigned(V[0].w, V[0].z); + V[0].w = transferred.x; + V[0].z = transferred.y; + + transferred = BitTransferSigned(V[1].y, V[1].x); + V[1].y = transferred.x; + V[1].x = transferred.y; + + transferred = BitTransferSigned(V[1].w, V[1].z); + V[1].w = transferred.x; + V[1].z = transferred.y; + if ((V[0].y + V[0].w + V[1].y) >= 0) { ep1 = ClampByte(ivec4(V[1].z, V[0].x, V[0].z, V[1].x)); ep2 = ClampByte(ivec4(V[1].w + V[1].z, V[0].x + V[0].y, V[0].z + V[0].w, V[1].x + V[1].y)); @@ -632,8 +868,86 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui } break; } + case 2: { + READ_UINT_VALUES(2) + uint y0, y1; + if (V[0].y >= V[0].x) { + y0 = V[0].x << 4u; + y1 = V[0].y << 4u; + } else { + y0 = (V[0].y << 4u) + 8u; + y1 = (V[0].x << 4u) - 8u; + } + ep1 = uvec4(0x780u, y0, y0, y0); + ep2 = uvec4(0x780u, y1, y1, y1); + break; + } + case 3: { + READ_UINT_VALUES(2) + uint y0, d; + if ((V[0].x & 0x80u) != 0u) { + y0 = ((V[0].y & 0xE0u) << 4u) | ((V[0].x & 0x7Fu) << 2u); + d = (V[0].y & 0x1Fu) << 2u; + } else { + y0 = ((V[0].y & 0xF0u) << 4u) | ((V[0].x & 0x7Fu) << 1u); + d = (V[0].y & 0x0Fu) << 1u; + } + const uint y1 = min(y0 + d, 0xFFFu); + ep1 = uvec4(0x780u, y0, y0, y0); + ep2 = uvec4(0x780u, y1, y1, y1); + break; + } + case 7: { + READ_UINT_VALUES(4) + ivec3 e0, e1; + DecodeHDREndpointMode7(V[0].x, V[0].y, V[0].z, V[0].w, e0, e1); + ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z)); + ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z)); + break; + } + case 11: { + READ_UINT_VALUES(6) + ivec3 e0, e1; + DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1); + ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z)); + ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z)); + break; + } + case 14: { + READ_UINT_VALUES(8) + ivec3 e0, e1; + DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1); + ep1 = uvec4(V[1].z, uint(e0.x), uint(e0.y), uint(e0.z)); + ep2 = uvec4(V[1].w, uint(e1.x), uint(e1.y), uint(e1.z)); + break; + } + case 15: { + READ_UINT_VALUES(8) + ivec3 e0, e1; + DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1); + const uint mode = ((V[1].z >> 7u) & 1u) | ((V[1].w >> 6u) & 2u); + int a6 = int(V[1].z & 0x7Fu); + int a7 = int(V[1].w & 0x7Fu); + int alpha0, alpha1; + if (mode == 3u) { + alpha0 = a6 << 5; + alpha1 = a7 << 5; + } else { + a6 |= (a7 << int(mode + 1u)) & 0x780; + a7 &= int(0x3Fu >> mode); + a7 ^= int(0x20u >> mode); + a7 -= int(0x20u >> mode); + a6 <<= int(4u - mode); + a7 <<= int(4u - mode); + a7 += a6; + alpha0 = a6; + alpha1 = clamp(a7, 0, 0xFFF); + } + ep1 = uvec4(uint(alpha0), uint(e0.x), uint(e0.y), uint(e0.z)); + ep2 = uvec4(uint(alpha1), uint(e1.x), uint(e1.y), uint(e1.z)); + break; + } default: { - // HDR mode, or more likely a bug computing the color_endpoint_mode ep1 = uvec4(0xFF, 0xFF, 0, 0); ep2 = uvec4(0xFF, 0xFF, 0, 0); break; @@ -651,19 +965,19 @@ uint UnquantizeTexelWeight(EncodingData val) { : FastReplicateTo6(bitval, bitlen); } else if (encoding == TRIT || encoding == QUINT) { uint B = 0, C = 0, D = 0; - uint b_mask = (0x3100 >> (bitlen << 2)) & 0xf; + uint b_mask = (0x3100 >> (bitlen * 4)) & 0xf; uint b = (bitval >> 1) & b_mask; D = QuintTritValue(val); if (encoding == TRIT) { switch (bitlen) { - case 0: return D << 5; //0,32,64 + case 0: return D * 32; //0,32,64 case 1: C = 50; break; case 2: C = 23; B = (b << 6) | (b << 2) | b; break; case 3: C = 11; B = (b << 5) | b; break; } } else if (encoding == QUINT) { switch (bitlen) { - case 0: return D << 4; //0, 16, 32, 48, 64 + case 0: return D * 16; //0, 16, 32, 48, 64 case 1: C = 28; break; case 2: C = 13; B = (b << 6) | (b << 1); break; } @@ -679,49 +993,107 @@ void UnquantizeTexelWeights(uvec2 size, bool is_dual_plane) { const uint num_planes = is_dual_plane ? 2 : 1; const uint area = size.x * size.y; const uint loop_count = min(result_index, area * num_planes); - for (uint i = 0; i < loop_count; ++i) { - result_vector[i] = UnquantizeTexelWeight(EncodingData(result_vector[i])); + for (uint itr = 0; itr < loop_count; ++itr) { + result_vector[itr] = + UnquantizeTexelWeight(GetEncodingFromVector(itr)); } } uint GetUnquantizedTexelWeight(uint offset_base, uint plane, bool is_dual_plane) { - const uint offset = is_dual_plane ? (offset_base << 1) + plane : offset_base; + const uint offset = is_dual_plane ? 2 * offset_base + plane : offset_base; return result_vector[offset]; } -uvec4 GetUnquantizedWeightVector(uvec2 pos, uvec2 size, uint plane_index, bool is_dual_plane) { +uvec4 GetUnquantizedWeightVector(uint t, uint s, uvec2 size, uint plane_index, bool is_dual_plane) { + const uint Ds = uint((block_dims.x * 0.5f + 1024) / (block_dims.x - 1)); + const uint Dt = uint((block_dims.y * 0.5f + 1024) / (block_dims.y - 1)); const uint area = size.x * size.y; - const uvec2 D = uvec2((block_dims * 0.5f + 1024.0f) / (block_dims - 1.0f)); - const uvec2 c = D * pos; - const uvec2 g = (c * (size - 1) + 32) >> 6; - const uvec2 j = g >> 4; - const uvec2 f = g & 0xF; - const uint w11 = (f.x * f.y + 8) >> 4; - const uint w10 = f.y - w11; - const uint w01 = f.x - w11; - const uint w00 = 16 - f.x - f.y + w11; + + const uint cs = Ds * s; + const uint ct = Dt * t; + const uint gs = (cs * (size.x - 1) + 32) >> 6; + const uint gt = (ct * (size.y - 1) + 32) >> 6; + const uint js = gs >> 4; + const uint fs = gs & 0xF; + const uint jt = gt >> 4; + const uint ft = gt & 0x0F; + const uint w11 = (fs * ft + 8) >> 4; + const uint w10 = ft - w11; + const uint w01 = fs - w11; + const uint w00 = 16 - fs - ft + w11; const uvec4 w = uvec4(w00, w01, w10, w11); - const uint v0 = j.y * size.x + j.x; - // - const uvec4 offset_base = uvec4(v0, v0 + 1, v0 + size.x, v0 + size.x + 1); - const uvec4 cmp_mask = uvec4(0) - uvec4(lessThan(offset_base, uvec4(area))); - uvec4 p0 = uvec4( - GetUnquantizedTexelWeight(offset_base.x, 0, is_dual_plane) & cmp_mask.x, - GetUnquantizedTexelWeight(offset_base.y, 0, is_dual_plane) & cmp_mask.y, - GetUnquantizedTexelWeight(offset_base.z, 0, is_dual_plane) & cmp_mask.z, - GetUnquantizedTexelWeight(offset_base.w, 0, is_dual_plane) & cmp_mask.w - ); - uvec4 p1 = uvec4( - GetUnquantizedTexelWeight(offset_base.x, 1, is_dual_plane) & cmp_mask.x, - GetUnquantizedTexelWeight(offset_base.y, 1, is_dual_plane) & cmp_mask.y, - GetUnquantizedTexelWeight(offset_base.z, 1, is_dual_plane) & cmp_mask.z, - GetUnquantizedTexelWeight(offset_base.w, 1, is_dual_plane) & cmp_mask.w - ); - const uvec2 final_weight = (uvec2(uint(dot(p0, w)), uint(dot(p1, w))) + 8) >> 4; - const uvec4 plane_mask = uvec4(0) - (uvec4(equal(uvec4(((plane_index + 1u) & 3u)), uvec4(0, 1, 2, 3))) & uvec4(0 - uint(is_dual_plane))); - return (final_weight.yyyy & plane_mask) | (final_weight.xxxx & ~plane_mask); + const uint v0 = jt * size.x + js; + + uvec4 p0 = uvec4(0); + uvec4 p1 = uvec4(0); + + if (v0 < area) { + const uint offset_base = v0; + p0.x = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.x = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); + } + if ((v0 + 1) < (area)) { + const uint offset_base = v0 + 1; + p0.y = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.y = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); + } + if ((v0 + size.x) < (area)) { + const uint offset_base = v0 + size.x; + p0.z = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.z = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); + } + if ((v0 + size.x + 1) < (area)) { + const uint offset_base = v0 + size.x + 1; + p0.w = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.w = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); + } + + const uint primary_weight = (uint(dot(p0, w)) + 8) >> 4; + + uvec4 weight_vec = uvec4(primary_weight); + + if (is_dual_plane) { + const uint secondary_weight = (uint(dot(p1, w)) + 8) >> 4; + for (uint c = 0; c < 4; c++) { + const bool is_secondary = ((plane_index + 1u) & 3u) == c; + weight_vec[c] = is_secondary ? secondary_weight : primary_weight; + } + } + return weight_vec; } +int FindLayout(uint mode) { + if ((mode & 3) != 0) { + if ((mode & 8) != 0) { + if ((mode & 4) != 0) { + if ((mode & 0x100) != 0) { + return 4; + } + return 3; + } + return 2; + } + if ((mode & 4) != 0) { + return 1; + } + return 0; + } + if ((mode & 0x100) != 0) { + if ((mode & 0x80) != 0) { + if ((mode & 0x20) != 0) { + return 8; + } + return 7; + } + return 9; + } + if ((mode & 0x80) != 0) { + return 6; + } + return 5; +} + + void FillError(ivec3 coord) { for (uint j = 0; j < block_dims.y; j++) { for (uint i = 0; i < block_dims.x; i++) { @@ -730,13 +1102,12 @@ void FillError(ivec3 coord) { } } -void FillVoidExtentLDR(uvec4 local_buff, ivec3 coord) { - // TODO: If you do extract bits, remember that it may be 11, or OTHER +void FillVoidExtentLDR(ivec3 coord) { SkipBits(52); - const uint r_u = StreamBits(local_buff, 16); - const uint g_u = StreamBits(local_buff, 16); - const uint b_u = StreamBits(local_buff, 16); - const uint a_u = StreamBits(local_buff, 16); + const uint r_u = StreamBits(16); + const uint g_u = StreamBits(16); + const uint b_u = StreamBits(16); + const uint a_u = StreamBits(16); const float a = float(a_u) / 65535.0f; const float r = float(r_u) / 65535.0f; const float g = float(g_u) / 65535.0f; @@ -748,13 +1119,13 @@ void FillVoidExtentLDR(uvec4 local_buff, ivec3 coord) { } } -bool IsError(uvec4 local_buff, uint mode) { +bool IsError(uint mode) { if ((mode & 0x1ff) == 0x1fc) { if ((mode & 0x200) != 0) { // params.void_extent_hdr = true; return true; } - if ((mode & 0x400) == 0 || StreamBits(local_buff, 1) == 0) { + if ((mode & 0x400) == 0 || StreamBits(1) == 0) { return true; } return false; @@ -768,36 +1139,9 @@ bool IsError(uvec4 local_buff, uint mode) { return false; } -int FindLayout(uint mode) { - // Possible (Relevant), x = dont care, (in hex) - // Before shift: 02x, 08x, 10x, 00x, 0Ax, 12x, 18x, 1Ax - // After shift: 01, 04, 08, 00, 05, 09, 0c, 0d - // Reassemble and remove middle 0x040 (moronic fuzzy table) - // 0000 -> 0 0020 -> 1 0040 -> 0 0060 -> 1 - // 0080 -> 2 00a0 -> 3 00c0 -> 2 00e0 -> 3 - // 0100 -> 4 0120 -> 5 0140 -> 4 0160 -> 5 - // 0180 -> 6 01a0 -> 7 01c0 -> 6 01e0 -> 7 - // Key ranges: 01a0 -> 7, 0180 -> 6, 0100 -> 4, 0080 -> 2 - // 8>>2 = 2, 4>>2 = 1 :: () = 0, (4) = 1, (8) = 2, (8,4) = 3 - const uint mask = 0 - uint((mode & 3) != 0); - const uint sh3_mode = (mode >> 2) & 3; - const uint sh0_mode = ((mode >> 6) & 6) | ((mode >> 5) & 1); - const uint fl_const_table = 0 - | ((1) << (2 * 4)) //0080 -> 2, 1 + 5 = 6 - | ((1) << (3 * 4)) - | ((4) << (4 * 4)) //0100 -> 4, 4 + 5 = 9 - | ((4) << (5 * 4)) - | ((2) << (6 * 4)) //0180 -> 6, 2 + 5 = 7 - | ((3) << (7 * 4)) //01a0 -> 7, 3 + 5 = 8 - ; - const uint if_mode3_t = sh3_mode + uint((mode & 0x10c) == 0x10c); - const uint if_mode3_f = 5 + ((fl_const_table >> (sh0_mode << 2)) & 7); - return int((if_mode3_t & mask) | (if_mode3_f & ~mask)); -} - -uvec2 DecodeBlockSize(uint mode_layout, uint mode) { +uvec2 DecodeBlockSize(uint mode) { uint A, B; - switch (mode_layout) { + switch (FindLayout(mode)) { case 0: A = (mode >> 5) & 0x3; B = (mode >> 7) & 0x3; @@ -837,81 +1181,130 @@ uvec2 DecodeBlockSize(uint mode_layout, uint mode) { } } -uint DecodeMaxWeight(uint mode_layout, uint mode) { - const uint mode0_or = (mode & 0x3) << 1; - const uint mode5_or = (mode & 0xc) >> 1; - const uint cmp_moden = 0 - uint(mode_layout < 5); - const uint cmp_add6 = 0 - (uint(mode_layout != 9) & (mode >> 9) & 1); - return (((mode >> 4) & 1) - | (mode0_or & cmp_moden) | (mode5_or & ~cmp_moden)) - + (6 & cmp_add6) - 1; +uint DecodeMaxWeight(uint mode) { + const uint mode_layout = FindLayout(mode); + uint weight_index = (mode & 0x10) != 0 ? 1 : 0; + if (mode_layout < 5) { + weight_index |= (mode & 0x3) << 1; + } else { + weight_index |= (mode & 0xc) >> 1; + } + weight_index -= 2; + if ((mode_layout != 9) && ((mode & 0x200) != 0)) { + weight_index += 6; + } + return weight_index + 1; } -void DecompressBlock(uvec4 local_buff, ivec3 coord) { - uint mode = StreamBits(local_buff, 11); - if (IsError(local_buff, mode)) { +void DecompressBlock(ivec3 coord) { + uint mode = StreamBits(11); + if (IsError(mode)) { FillError(coord); return; } if ((mode & 0x1ff) == 0x1fc) { // params.void_extent_ldr = true; - FillVoidExtentLDR(local_buff, coord); + FillVoidExtentLDR(coord); return; } - const uint num_partitions = StreamBits(local_buff, 2) + 1; + const uvec2 size_params = DecodeBlockSize(mode); + if ((size_params.x > block_dims.x) || (size_params.y > block_dims.y)) { + FillError(coord); + return; + } + const uint num_partitions = StreamBits(2) + 1; const uint mode_layout = FindLayout(mode); const bool dual_plane = (mode_layout != 9) && ((mode & 0x400) != 0); - const uvec2 size_params = DecodeBlockSize(mode_layout, mode); - if (size_params.x > block_dims.x || size_params.y > block_dims.y - || num_partitions > 4 || (num_partitions == 4 && dual_plane) - || GetNumWeightValues(size_params, dual_plane) > MAX_WEIGHT_VALUES) { + if (num_partitions > 4 || (num_partitions == 4 && dual_plane)) { + FillError(coord); + return; + } + if (GetNumWeightValues(size_params, dual_plane) > MAX_WEIGHT_VALUES) { FillError(coord); return; } uint partition_index = 1; uvec4 color_endpoint_mode = uvec4(0); + uint ced_pointer = 0; uint base_cem = 0; if (num_partitions == 1) { - color_endpoint_mode.x = StreamBits(local_buff, 4); + color_endpoint_mode.x = StreamBits(4); partition_index = 0; } else { - partition_index = StreamBits(local_buff, 10); - base_cem = StreamBits(local_buff, 6); + partition_index = StreamBits(10); + base_cem = StreamBits(6); } const uint base_mode = base_cem & 3; - const uint max_weight = DecodeMaxWeight(mode_layout, mode); + const uint max_weight = DecodeMaxWeight(mode); const uint weight_bits = GetPackedBitSize(size_params, dual_plane, max_weight); - const uint extra_cem_bits = base_mode > 0 ? ((0x85200 >> (num_partitions << 2)) & 0x0f) : 0; - const uint plane_selector_bits = dual_plane ? 2 : 0; + if (weight_bits < 24 || weight_bits > 96) { + FillError(coord); + return; + } uint remaining_bits = 128 - weight_bits - total_bitsread; + uint extra_cem_bits = 0; + if (base_mode > 0) { + switch (num_partitions) { + case 2: + extra_cem_bits += 2; + break; + case 3: + extra_cem_bits += 5; + break; + case 4: + extra_cem_bits += 8; + break; + default: + FillError(coord); + return; + } + } remaining_bits -= extra_cem_bits; + const uint plane_selector_bits = dual_plane ? 2 : 0; remaining_bits -= plane_selector_bits; if (remaining_bits > 128) { // Bad data, more remaining bits than 4 bytes // return early + FillError(coord); return; } // Read color data... const uint color_data_bits = remaining_bits; - for (uint i = 0; remaining_bits > 0 && i < 4; ++i) { + while (remaining_bits > 0) { const int nb = int(min(remaining_bits, 32U)); - color_endpoint_data[i] = StreamBits(local_buff, nb); + const uint b = StreamBits(nb); + color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb)); + ++ced_pointer; remaining_bits -= nb; } - - // color_endpoint_mode assumed to be 0 on invalids/out of "range" - const uint plane_index = uint(StreamBits(local_buff, plane_selector_bits)); - const uvec4 cem_mask = (uvec4(0, 1, 2, 3) - num_partitions) >> 8; + const uint plane_index = uint(StreamBits(plane_selector_bits)); if (base_mode > 0) { - const uint extra_cem = StreamBits(local_buff, extra_cem_bits); - const uint cem = ((extra_cem << 6) | base_cem) >> 2; - const uint c0 = cem & ((1 << num_partitions) - 1); - const uint c1 = (cem >> num_partitions) & ((1 << (num_partitions << 1)) - 1); - const uvec4 c = (uvec4(c0) >> uvec4(0, 1, 2, 3)) & 1; - const uvec4 m = (uvec4(c1) >> uvec4(0, 2, 4, 6)) & 3; - color_endpoint_mode = (((uvec4(base_mode) - (1 - c)) << 2) | m) & cem_mask; + const uint extra_cem = StreamBits(extra_cem_bits); + uint cem = (extra_cem << 6) | base_cem; + cem >>= 2; + uvec4 C = uvec4(0); + for (uint i = 0; i < num_partitions; i++) { + C[i] = (cem & 1); + cem >>= 1; + } + uvec4 M = uvec4(0); + for (uint i = 0; i < num_partitions; i++) { + M[i] = cem & 3; + cem >>= 2; + } + for (uint i = 0; i < num_partitions; i++) { + color_endpoint_mode[i] = base_mode; + if (C[i] == 0) { + --color_endpoint_mode[i]; + } + color_endpoint_mode[i] <<= 2; + color_endpoint_mode[i] |= M[i]; + } } else if (num_partitions > 1) { - color_endpoint_mode = uvec4(base_cem >> 2) & cem_mask; + const uint cem = base_cem >> 2; + for (uint i = 0; i < num_partitions; i++) { + color_endpoint_mode[i] = cem; + } } uvec4 endpoints0[4]; @@ -922,46 +1315,90 @@ void DecompressBlock(uvec4 local_buff, ivec3 coord) { uint color_values[32]; uint colvals_index = 0; DecodeColorValues(color_endpoint_mode, num_partitions, color_data_bits, color_values); - for (uint i = 0; i < num_partitions; i++) - ComputeEndpoints(endpoints0[i], endpoints1[i], color_endpoint_mode[i], color_values, colvals_index); + for (uint i = 0; i < num_partitions; i++) { + ComputeEndpoints(endpoints0[i], endpoints1[i], color_endpoint_mode[i], color_values, + colvals_index); + } } color_endpoint_data = local_buff; color_endpoint_data = bitfieldReverse(color_endpoint_data).wzyx; const uint clear_byte_start = (weight_bits >> 3) + 1; - const uint byte_insert = ExtractBits(color_endpoint_data, (clear_byte_start - 1) << 3, 8) & uint(((1 << (weight_bits & 7)) - 1)); + const uint byte_insert = ExtractBits(color_endpoint_data, int(clear_byte_start - 1) * 8, 8) & + uint(((1 << (weight_bits % 8)) - 1)); const uint vec_index = (clear_byte_start - 1) >> 2; - color_endpoint_data[vec_index] = bitfieldInsert(color_endpoint_data[vec_index], byte_insert, int((clear_byte_start - 1) & 3) << 3, 8); - for (uint i = clear_byte_start; i < 16; ++i) - color_endpoint_data[i >> 2] = bitfieldInsert(color_endpoint_data[i >> 2], 0, int(i & 3) << 3, 8); + color_endpoint_data[vec_index] = bitfieldInsert(color_endpoint_data[vec_index], byte_insert, + int((clear_byte_start - 1) % 4) * 8, 8); + for (uint i = clear_byte_start; i < 16; ++i) { + const uint idx = i >> 2; + color_endpoint_data[idx] = bitfieldInsert(color_endpoint_data[idx], 0, int(i % 4) * 8, 8); + } // Re-init vector variables for next decode phase result_index = 0; color_bitsread = 0; + result_limit_reached = false; // The limit for the Unquantize phase, avoids decoding more data than needed. - result_vector_max_index = (size_params.x * size_params.y) << uint(dual_plane); + result_vector_max_index = size_params.x * size_params.y; + if (dual_plane) { + result_vector_max_index *= 2; + } DecodeIntegerSequence(max_weight, GetNumWeightValues(size_params, dual_plane)); + UnquantizeTexelWeights(size_params, dual_plane); for (uint j = 0; j < block_dims.y; j++) { for (uint i = 0; i < block_dims.x; i++) { - const uint local_partition = Select2DPartition(partition_index, uvec2(i, j), num_partitions) & (0 - uint(num_partitions > 1)); - const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]); - const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]); - const uvec4 weight_vec = GetUnquantizedWeightVector(uvec2(i, j), size_params, plane_index, dual_plane); - const vec4 Cf = vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + 32) >> 6); - const vec4 p = (Cf / 65535.0f); - imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar); + uint local_partition = 0; + if (num_partitions > 1) { + local_partition = Select2DPartition(partition_index, i, j, num_partitions); + } + const uint local_cem = color_endpoint_mode[local_partition]; + const uvec4 weight_vec = GetUnquantizedWeightVector(j, i, size_params, plane_index, dual_plane); + + vec4 p; + if (IsHDRColorEndpointMode(local_cem)) { + const uvec4 C0 = endpoints0[local_partition] << 4u; + const uvec4 C1 = endpoints1[local_partition] << 4u; + const uvec4 C = (C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64u; + const uvec4 E = (C & uvec4(0xF800u)) >> 11u; + const uvec4 M = C & uvec4(0x7FFu); + const uvec4 Mt_lo = 3u * M; + const uvec4 Mt_mid = 4u * M - 512u; + const uvec4 Mt_hi = 5u * M - 2048u; + const uvec4 Mt = + mix(Mt_lo, mix(Mt_mid, Mt_hi, greaterThanEqual(M, uvec4(1536u))), + greaterThanEqual(M, uvec4(512u))); + const uvec4 Cf = (E << 10u) + (Mt >> 3u); + const uvec4 half_bits = mix(Cf, uvec4(0x7BFFu), greaterThanEqual(Cf, uvec4(0x7C00u))); + p = vec4(unpackHalf2x16(half_bits.x).x, unpackHalf2x16(half_bits.y).x, + unpackHalf2x16(half_bits.z).x, unpackHalf2x16(half_bits.w).x); + + if (local_cem == 14u) { + const uint a0 = ReplicateByteTo16(uvec4(endpoints0[local_partition].x)).x; + const uint a1 = ReplicateByteTo16(uvec4(endpoints1[local_partition].x)).x; + const uint Ca = (a0 * (64u - weight_vec.x) + a1 * weight_vec.x + 32u) / 64u; + p.x = float(Ca) / 65535.0f; + } + } else { + const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]); + const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]); + const vec4 Cf = + vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64); + p = Cf / 65535.0f; + } + + imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar); } } } uint SwizzleOffset(uvec2 pos) { - return ((pos.x & 32u) << 3u) - | ((pos.y & 6u) << 5u) - | ((pos.x & 16u) << 1u) - | ((pos.y & 1u) << 4u) - | (pos.x & 15u); + return ((pos.x & 32u) << 3u) | + ((pos.y & 6u) << 5u) | + ((pos.x & 16u) << 1u) | + ((pos.y & 1u) << 4u) | + (pos.x & 15u); } void main() { @@ -981,5 +1418,6 @@ void main() { if (any(greaterThanEqual(coord, imageSize(dest_image)))) { return; } - DecompressBlock(astc_data[offset >> 4], coord); + local_buff = astc_data[offset / 16]; + DecompressBlock(coord); } diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp index 9cb0f2aca2..2a3446ca66 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp @@ -51,7 +51,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u1 layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly uimage2DArray output_image; -layout(local_size_x = 32, local_size_y = 8, local_size_z = 1) in; +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; const uint GOB_SIZE_X = 64; const uint GOB_SIZE_Y = 8; @@ -65,11 +65,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + uint SwizzleOffset(uvec2 pos) { pos = pos & SWIZZLE_MASK; - return ((pos.x & 32u) << 3u) | ((pos.y & 6u) << 5u) | - ((pos.x & 16u) << 1u) | ((pos.y & 1u) << 4u) | - (pos.x & 15u); + return SwizzleTable(pos.y * 64 + pos.x); } uvec4 ReadTexel(uint offset) { diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d.comp index dc23b32492..34e4f93307 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_3d.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d.comp @@ -53,7 +53,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u1 layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly uimage3D output_image; -layout(local_size_x = 16, local_size_y = 8, local_size_z = 2) in; +layout(local_size_x = 16, local_size_y = 8, local_size_z = 8) in; const uint GOB_SIZE_X = 64; const uint GOB_SIZE_Y = 8; @@ -67,11 +67,19 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + uint SwizzleOffset(uvec2 pos) { pos = pos & SWIZZLE_MASK; - return ((pos.x & 32u) << 3u) | ((pos.y & 6u) << 5u) | - ((pos.x & 16u) << 1u) | ((pos.y & 1u) << 4u) | - (pos.x & 15u); + return SwizzleTable(pos.y * 64 + pos.x); } uvec4 ReadTexel(uint offset) { diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp new file mode 100644 index 0000000000..e084837f25 --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#version 430 + +#ifdef VULKAN + #extension GL_EXT_shader_16bit_storage : require + #extension GL_EXT_shader_8bit_storage : require + #define HAS_EXTENDED_TYPES 1 + #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { + #define END_PUSH_CONSTANTS }; + #define UNIFORM(n) + #define BINDING_INPUT_BUFFER 0 + #define BINDING_OUTPUT_BUFFER 1 +#else + #extension GL_NV_gpu_shader5 : enable + #ifdef GL_NV_gpu_shader5 + #define HAS_EXTENDED_TYPES 1 + #else + #define HAS_EXTENDED_TYPES 0 + #endif + #define BEGIN_PUSH_CONSTANTS + #define END_PUSH_CONSTANTS + #define UNIFORM(n) layout(location = n) uniform + #define BINDING_INPUT_BUFFER 1 + #define BINDING_OUTPUT_BUFFER 0 +#endif + +// --- Push Constants / Uniforms --- +#ifdef VULKAN +layout(push_constant) uniform PushConstants { + uvec3 blocks_dim; // Offset 0 + uint bytes_per_block_log2; // Offset 12 + + uvec3 origin; // Offset 16 + uint slice_size; // Offset 28 + + uint block_size; // Offset 32 + uint x_shift; // Offset 36 + uint block_height; // Offset 40 + uint block_height_mask; // Offset 44 + + uint block_depth; // Offset 48 + uint block_depth_mask; // Offset 52 + int _pad; // Offset 56 + + ivec3 destination; // Offset 60 +} pc; +#else +BEGIN_PUSH_CONSTANTS + UNIFORM(0) uvec3 origin; + UNIFORM(1) ivec3 destination; + UNIFORM(2) uint bytes_per_block_log2; + UNIFORM(3) uint slice_size; + UNIFORM(4) uint block_size; + UNIFORM(5) uint x_shift; + UNIFORM(6) uint block_height; + UNIFORM(7) uint block_height_mask; + UNIFORM(8) uint block_depth; + UNIFORM(9) uint block_depth_mask; + UNIFORM(10) uvec3 blocks_dim; +END_PUSH_CONSTANTS +#define pc // Map pc prefix to nothing for OpenGL compatibility +#endif + +// --- Buffers --- +#if HAS_EXTENDED_TYPES +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; +#endif +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer { + uint out_u32[]; +}; + +// --- Constants --- +layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; +const uint GOB_SIZE_Z = 1; +const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_Z_SHIFT = 0; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +uint SwizzleTable(uint pos) { + const uint t[8] = uint[]( + 0x12100200, 0x13110301, 0x16140604, 0x17150705, + 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d + ); + const uint i = pos >> 4; + const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff; + return (h << 4) | (pos & 0xf); +} + +// --- Helpers --- +uint SwizzleOffset(uvec2 pos) { + pos &= SWIZZLE_MASK; + return SwizzleTable(pos.y * 64u + pos.x); +} + +uvec4 ReadTexel(uint offset) { + uint bpl2 = pc.bytes_per_block_log2; + switch (bpl2) { +#if HAS_EXTENDED_TYPES + case 0u: return uvec4(u8data[offset], 0u, 0u, 0u); + case 1u: return uvec4(u16data[offset / 2u], 0u, 0u, 0u); +#else + case 0u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 24u), 8), 0u, 0u, 0u); + case 1u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 16u), 16), 0u, 0u, 0u); +#endif + case 2u: return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 block_coord = gl_GlobalInvocationID; + if (any(greaterThanEqual(block_coord, pc.blocks_dim))) { + return; + } + + uint bytes_per_block = 1u << pc.bytes_per_block_log2; + // Origin is in pixels, divide by 4 for block-space (e.g. BCn formats) + uvec3 pos; + pos.x = (block_coord.x + (pc.origin.x >> 2u)) * bytes_per_block; + pos.y = block_coord.y + (pc.origin.y >> 2u); + pos.z = block_coord.z + pc.origin.z; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + uint offset = 0u; + // Apply block-linear offsets + offset += (pos.z >> pc.block_depth) * pc.slice_size; + offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height); + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + // Calculate linear output index + uint block_index = block_coord.x + + (block_coord.y * pc.blocks_dim.x) + + (block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y); + uint out_idx = block_index * (bytes_per_block >> 2u); + + out_u32[out_idx] = texel.x; + out_u32[out_idx + 1u] = texel.y; + if (pc.bytes_per_block_log2 == 4u) { + out_u32[out_idx + 2u] = texel.z; + out_u32[out_idx + 3u] = texel.w; + } +} \ No newline at end of file diff --git a/src/video_core/host_shaders/pitch_unswizzle.comp b/src/video_core/host_shaders/pitch_unswizzle.comp index dba874b414..7d23f594c7 100644 --- a/src/video_core/host_shaders/pitch_unswizzle.comp +++ b/src/video_core/host_shaders/pitch_unswizzle.comp @@ -33,7 +33,7 @@ BEGIN_PUSH_CONSTANTS UNIFORM(0) uvec2 origin; UNIFORM(1) ivec2 destination; -UNIFORM(2) uint bytes_per_block_log2; +UNIFORM(2) uint bytes_per_block; UNIFORM(3) uint pitch; END_PUSH_CONSTANTS @@ -47,26 +47,26 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU128 { layout(binding = BINDING_OUTPUT_IMAGE) writeonly uniform uimage2D output_image; -layout(local_size_x = 32, local_size_y = 8, local_size_z = 1) in; +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; uvec4 ReadTexel(uint offset) { - switch (bytes_per_block_log2) { + switch (bytes_per_block) { #if HAS_EXTENDED_TYPES - case 0: - return uvec4(u8data[offset], 0, 0, 0); case 1: + return uvec4(u8data[offset], 0, 0, 0); + case 2: return uvec4(u16data[offset / 2], 0, 0, 0); #else - case 0: - return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 24), 8), 0, 0, 0); case 1: + return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 24), 8), 0, 0, 0); + case 2: return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 16), 16), 0, 0, 0); #endif - case 2: - return uvec4(u32data[offset / 4], 0, 0, 0); - case 3: - return uvec4(u64data[offset / 8], 0, 0); case 4: + return uvec4(u32data[offset / 4], 0, 0, 0); + case 8: + return uvec4(u64data[offset / 8], 0, 0); + case 16: return u128data[offset / 16]; } return uvec4(0); @@ -76,7 +76,7 @@ void main() { uvec2 pos = gl_GlobalInvocationID.xy + origin; uint offset = 0; - offset += pos.x << bytes_per_block_log2; + offset += pos.x * bytes_per_block; offset += pos.y * pitch; const uvec4 texel = ReadTexel(offset); diff --git a/src/video_core/host_shaders/vulkan_color_clear.frag b/src/video_core/host_shaders/vulkan_color_clear.frag index d17ce12b1a..617bf01e17 100644 --- a/src/video_core/host_shaders/vulkan_color_clear.frag +++ b/src/video_core/host_shaders/vulkan_color_clear.frag @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,10 +7,8 @@ layout (push_constant) uniform PushConstants { vec4 clear_color; }; -layout(location = 0) out vec4 colors[8]; +layout(location = 0) out vec4 color; void main() { - for (int index = 0; index < 8; ++index) { - colors[index] = clear_color; - } + color = clear_color; } diff --git a/src/video_core/host_shaders/vulkan_indirect_quads.comp b/src/video_core/host_shaders/vulkan_indirect_quads.comp deleted file mode 100644 index 01bc2f961f..0000000000 --- a/src/video_core/host_shaders/vulkan_indirect_quads.comp +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#version 460 core - -layout (local_size_x = 32) in; - -layout (std430, set = 0, binding = 0) readonly buffer InputBuffer { - uint input_commands[]; -}; - -layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer { - uint output_commands[]; -}; - -layout (push_constant) uniform PushConstants { - uint num_draws; - uint src_stride; -}; - -void main() { - uint draw = gl_GlobalInvocationID.x; - if (draw >= num_draws) { - return; - } - uint src = draw * src_stride; - uint dst = draw * 5; - output_commands[dst + 0] = input_commands[src + 0] / 4 * 6; - output_commands[dst + 1] = input_commands[src + 1]; - output_commands[dst + 2] = input_commands[src + 2] / 4 * 6; - output_commands[dst + 3] = input_commands[src + 3]; - output_commands[dst + 4] = input_commands[src + 4]; -} diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index b9b136f6b8..8ffdcb6276 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -46,8 +46,8 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& page_table_mask = page_table_size - 1; big_page_table_mask = big_page_table_size - 1; + big_page_table_dev.ResizeAndClear(big_page_table_size); big_entries.resize(big_page_table_size / 32, 0); - big_page_table_dev.resize(big_page_table_size); big_page_continuous.resize(big_page_table_size / continuous_bits, 0); entries.resize(page_table_size / 32, 0); } @@ -143,7 +143,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr const DAddr current_dev_addr = dev_addr + offset; const auto index = PageEntryIndex(current_gpu_addr, true); const u32 sub_value = static_cast(current_dev_addr >> cpu_page_bits); - big_page_table_dev[index] = sub_value; + big_page_table_dev.Set(index, sub_value); const bool is_continuous = ([&] { uintptr_t base_ptr{ reinterpret_cast(memory.GetPointer(current_dev_addr))}; @@ -713,7 +713,7 @@ void MemoryManager::FlushCaching() { if (accumulator.InvalidateAll([this](GPUVAddr addr, size_t size) { GetSubmappedRangeImpl(addr, size, page_stash2); })) { - rasterizer->InnerInvalidation(VideoCommon::FixSmallVectorADL(page_stash2)); + rasterizer->InnerInvalidation(page_stash2); page_stash2.clear(); } } diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index f9fddd177a..08978567d2 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -17,7 +17,7 @@ #include "common/multi_level_page_table.h" #include "common/range_map.h" #include "common/scratch_buffer.h" -#include "common/virtual_buffer.h" +#include "common/sparse_large_vector.h" #include "video_core/invalidation_accumulator.h" #include "video_core/cache_types.h" #include "video_core/host1x/gpu_device_memory_manager.h" @@ -214,7 +214,7 @@ private: Common::MultiLevelPageTable page_table; Common::RangeMap kind_map; - Common::VirtualBuffer big_page_table_dev; + Common::SparseLargeVector big_page_table_dev; std::vector big_page_continuous; boost::container::small_vector, 32> page_stash{}; diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 5404dec943..b8c1bc4f45 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -260,7 +260,7 @@ void QueryCacheBase::CounterReport(GPUVAddr addr, QueryType counter_type }; u8* pointer = impl->device_memory.template GetPointer(cpu_addr); u8* pointer_timestamp = impl->device_memory.template GetPointer(cpu_addr + 8); - bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence; + bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence; std::function operation([this, is_synced, streamer, query_base = query, query_location, pointer, pointer_timestamp] { if (True(query_base->flags & QueryFlagBits::IsInvalidated)) { diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index d0e55da8b1..b527c0e2a9 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -78,7 +78,7 @@ public: virtual void SyncOperation(std::function&& func) = 0; /// Signal a GPU based syncpoint as a fence - virtual void SignalSyncPoint(u32 value, u32 count) = 0; + virtual void SignalSyncPoint(u32 value) = 0; /// Signal a GPU based reference as point virtual void SignalReference() = 0; @@ -114,6 +114,12 @@ public: /// Notify rasterizer that any caches of the specified region are desync with guest virtual void OnCacheInvalidation(PAddr addr, u64 size) = 0; + virtual bool TryCacheInvalidation(DAddr addr, u64 size) { + if (MustFlushRegion(addr, size, VideoCommon::CacheType::BufferCache)) return false; + OnCacheInvalidation(addr, size); + return true; + } + virtual bool OnCPUWrite(PAddr addr, u64 size) = 0; /// Sync memory between guest and host. diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index d2fb89119c..a5cda0f389 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -72,12 +69,10 @@ void RasterizerNull::SignalFence(std::function&& func) { void RasterizerNull::SyncOperation(std::function&& func) { func(); } -void RasterizerNull::SignalSyncPoint(u32 value, u32 count) { +void RasterizerNull::SignalSyncPoint(u32 value) { auto& syncpoint_manager = m_gpu.Host1x().GetSyncpointManager(); - for (u32 i = 0; i < count; ++i) { - syncpoint_manager.IncrementGuest(value); - syncpoint_manager.IncrementHost(value); - } + syncpoint_manager.IncrementGuest(value); + syncpoint_manager.IncrementHost(value); } void RasterizerNull::SignalReference() {} void RasterizerNull::ReleaseFences(bool) {} diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index c98da41f15..c7f5849c75 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -64,7 +61,7 @@ public: void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; void SyncOperation(std::function&& func) override; - void SignalSyncPoint(u32 value, u32 count) override; + void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences(bool force) override; void FlushAndInvalidateRegion( diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index 434fc172e3..e6147d66cc 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,7 +20,7 @@ void RendererNull::Composite(std::span framebuff } m_gpu.RendererFrameEndNotify(); - render_window.OnFrameDisplayed(1); + render_window.OnFrameDisplayed(); } std::vector RendererNull::GetAppletCaptureBuffer() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 35d47856eb..1d77d28c46 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -615,8 +615,8 @@ void RasterizerOpenGL::SyncOperation(std::function&& func) { fence_manager.SyncOperation(std::move(func)); } -void RasterizerOpenGL::SignalSyncPoint(u32 value, u32 count) { - fence_manager.SignalSyncPoint(value, count); +void RasterizerOpenGL::SignalSyncPoint(u32 value) { + fence_manager.SignalSyncPoint(value); } void RasterizerOpenGL::SignalReference() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 338c5eb877..6eae51ff7d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -109,7 +106,7 @@ public: void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; void SyncOperation(std::function&& func) override; - void SignalSyncPoint(u32 value, u32 count) override; + void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences(bool force = true) override; void FlushAndInvalidateRegion( diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index df69eb97f5..277cf222fa 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -414,6 +414,11 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n if (!use_asynchronous_shaders) { return pipeline; } + // If something is using depth, we can assume that games are not rendering anything which + // will be used one time. + if (maxwell3d->regs.zeta_enable) { + return nullptr; + } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 2589e42109..680cb2179f 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -235,7 +235,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array swizzles) { + std::span swizzles, + u32 z_start, u32 z_count) { switch (image.info.type) { case ImageType::e2D: if (IsPixelFormatASTC(image.info.format)) { @@ -689,6 +709,11 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, gl_internal_format = is_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8; gl_format = GL_RGBA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + + if (IsPixelFormatASTC(info.format) && IsAstcRecompressionEnabled()) { + gl_internal_format = SelectAstcFormat(info.format, is_srgb); + gl_format = GL_NONE; + } } else { const auto& tuple = MaxwellToGL::GetFormatTuple(info.format); gl_internal_format = tuple.internal_format; @@ -1085,6 +1110,10 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI if (True(image.flags & ImageFlagBits::Converted)) { const bool is_srgb = IsPixelFormatSRGB(info.format); internal_format = is_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8; + + if (IsPixelFormatASTC(info.format) && IsAstcRecompressionEnabled()) { + internal_format = SelectAstcFormat(info.format, is_srgb); + } } else { internal_format = MaxwellToGL::GetFormatTuple(format).internal_format; } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 19a1b79cf8..8a830fa0c9 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -75,7 +75,9 @@ public: void Finish(); - StagingBufferMap UploadStagingBuffer(size_t size); + void FlushDeferredClear() {} + + StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false); @@ -119,7 +121,8 @@ public: Tegra::Engines::Fermi2D::Operation operation); void AccelerateImageUpload(Image& image, const StagingBufferMap& map, - std::span swizzles); + std::span swizzles, + u32 z_start, u32 z_count); void InsertUploadMemoryBarrier(); @@ -226,6 +229,8 @@ public: bool ScaleDown(bool ignore = false); + u64 allocation_tick; + private: void CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t buffer_offset); @@ -362,6 +367,7 @@ private: }; struct TextureCacheParams { + static constexpr bool ENABLE_VALIDATION = true; static constexpr bool FRAMEBUFFER_BLITS = true; static constexpr bool HAS_EMULATED_COPIES = true; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index caf15294b2..f9ec32e652 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -151,7 +151,7 @@ void RendererOpenGL::Composite(std::span framebu rasterizer.TickFrame(); context->SwapBuffers(); - render_window.OnFrameDisplayed(1); + render_window.OnFrameDisplayed(); } void RendererOpenGL::AddTelemetryFields() { diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index ef3fc05f2a..7ced8c77b4 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include @@ -117,7 +116,7 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map, std::span swizzles) { - static constexpr Extent3D WORKGROUP_SIZE{32, 8, 1}; + static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1}; static constexpr GLuint BINDING_INPUT_BUFFER = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; @@ -152,7 +151,7 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map, std::span swizzles) { - static constexpr Extent3D WORKGROUP_SIZE{16, 8, 2}; + static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8}; static constexpr GLuint BINDING_INPUT_BUFFER = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; @@ -190,12 +189,12 @@ void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map, void UtilShaders::PitchUpload(Image& image, const StagingBufferMap& map, std::span swizzles) { - static constexpr Extent3D WORKGROUP_SIZE{32, 8, 1}; + static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1}; static constexpr GLuint BINDING_INPUT_BUFFER = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; static constexpr GLuint LOC_ORIGIN = 0; static constexpr GLuint LOC_DESTINATION = 1; - static constexpr GLuint LOC_BYTES_PER_BLOCK_LOG2 = 2; + static constexpr GLuint LOC_BYTES_PER_BLOCK = 2; static constexpr GLuint LOC_PITCH = 3; const u32 bytes_per_block = BytesPerBlock(image.info.format); @@ -209,7 +208,7 @@ void UtilShaders::PitchUpload(Image& image, const StagingBufferMap& map, glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); glUniform2ui(LOC_ORIGIN, 0, 0); glUniform2i(LOC_DESTINATION, 0, 0); - glUniform1ui(LOC_BYTES_PER_BLOCK_LOG2, static_cast(std::countr_zero(bytes_per_block))); + glUniform1ui(LOC_BYTES_PER_BLOCK, bytes_per_block); glUniform1ui(LOC_PITCH, pitch); glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), 0, GL_FALSE, 0, GL_WRITE_ONLY, format); diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 7c91f53f32..ddd52e9426 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -279,44 +279,6 @@ constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_ONLY_STATE_CREATE .maxDepthBounds = 0.0f, }; -constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .depthTestEnable = VK_FALSE, - .depthWriteEnable = VK_FALSE, - .depthCompareOp = VK_COMPARE_OP_ALWAYS, - .depthBoundsTestEnable = VK_FALSE, - .stencilTestEnable = VK_FALSE, - .front = VkStencilOpState{}, - .back = VkStencilOpState{}, - .minDepthBounds = 0.0f, - .maxDepthBounds = 0.0f, -}; - -using BlendAttachments = std::array; - -[[nodiscard]] VkPipelineColorBlendStateCreateInfo MakeColorBlendState( - const BlendAttachments& attachments, const RenderingFormats& formats) { - return VkPipelineColorBlendStateCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_CLEAR, - .attachmentCount = formats.num_colors, - .pAttachments = attachments.data(), - .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, - }; -} - -[[nodiscard]] VkPipelineMultisampleStateCreateInfo MakeMultisampleState( - VkSampleCountFlagBits samples) { - VkPipelineMultisampleStateCreateInfo multisample_ci = PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisample_ci.rasterizationSamples = samples; - return multisample_ci; -} - template inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, @@ -490,19 +452,45 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) { }; } +void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, + VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { + constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = flags, + .dstAccessMask = flags, + .oldLayout = source_layout, + .newLayout = target_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, barrier); +} + void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) { const VkImage image = image_view.ImageHandle(); const VkImageSubresourceRange subresource_range = SubresourceRangeFromView(image_view); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([image, subresource_range](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -510,27 +498,20 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view) .image = image, .subresourceRange = subresource_range, }; - cmdbuf.PipelineBarrier(barrier); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + barrier); }); } -void DiscardCoveredAttachments(Scheduler& scheduler, const Framebuffer* framebuffer, - const Region2D& region, bool writes_stencil) { - const VkExtent2D area = framebuffer->RenderArea(); - if ((std::min)(region.start.x, region.end.x) > 0 || - (std::min)(region.start.y, region.end.y) > 0 || - (std::max)(region.start.x, region.end.x) < static_cast(area.width) || - (std::max)(region.start.y, region.end.y) < static_cast(area.height) || - framebuffer->Attachments().layers != 1) { - return; - } - u32 attachments = 1u | Scheduler::DEPTH_ATTACHMENT_BIT; - if (writes_stencil) { - attachments |= Scheduler::STENCIL_ATTACHMENT_BIT; - } - scheduler.OverrideLoadOps(framebuffer, attachments, VK_ATTACHMENT_LOAD_OP_DONT_CARE, {}); -} - [[nodiscard]] VkSampleCountFlagBits SampleCountFlag(u32 num_samples) { switch (num_samples) { case 2: @@ -581,6 +562,25 @@ void DiscardCoveredAttachments(Scheduler& scheduler, const Framebuffer* framebuf }, }); } + +void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) { + const VkRenderPass render_pass = framebuffer->RenderPass(); + const VkFramebuffer framebuffer_handle = framebuffer->Handle(); + const VkExtent2D render_area = framebuffer->RenderArea(); + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = render_pass, + .framebuffer = framebuffer_handle, + .renderArea{ + .offset{}, + .extent = render_area, + }, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); +} } // Anonymous namespace BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, @@ -664,10 +664,8 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV Tegra::Engines::Fermi2D::Operation operation) { const bool is_linear = filter == Tegra::Engines::Fermi2D::Filter::Bilinear; const BlitImagePipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .operation = operation, - .samples = dst_framebuffer->Samples(), - .target = 0, }; VkSampler sampler = *nearest_sampler; if (is_linear) { @@ -683,19 +681,16 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView const Region2D& dst_region, const Region2D& src_region, const Extent3D& src_size) { const BlitImagePipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy, - .samples = dst_framebuffer->Samples(), - .target = 0, }; const VkPipelineLayout layout = *one_texture_pipeline_layout; const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); - if (scheduler.IsRenderPassImage(src_image)) { - scheduler.RequestOutsideRenderPassOperationContext(); - } - scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([this, src_image_view, src_sampler, dst_region, src_region, src_size, - pipeline, layout](vk::CommandBuffer cmdbuf) { + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, + src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + BeginRenderPass(cmdbuf, dst_framebuffer); const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); @@ -703,8 +698,8 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView nullptr); BindBlitState(cmdbuf, layout, dst_region, src_region, src_size); cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); }); - scheduler.InvalidateState(); } void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer, @@ -718,7 +713,6 @@ void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer, } RecordShaderReadBarrier(scheduler, src_image_view); - DiscardCoveredAttachments(scheduler, dst_framebuffer, dst_region, blit_stencil); scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view, src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) { @@ -744,7 +738,7 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { const BlitMSAAPipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), }; BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, @@ -758,7 +752,7 @@ void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, const bool blit_stencil = dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported(); const BlitMSAAPipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .samples = dst_framebuffer->Samples(), }; VkImageView src_stencil_view = VK_NULL_HANDLE; @@ -773,7 +767,7 @@ void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view, const Region2D& dst_region, const Region2D& src_region) { BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, - FindOrEmplaceBlitDepthPipeline(dst_framebuffer->Formats()), *nearest_sampler, + FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler, src_image_view.DepthView(), VK_NULL_HANDLE, false); } @@ -787,7 +781,7 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer, src_stencil_view = src_image_view.StencilView(); } BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, - FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->Formats(), + FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil), *nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil); } @@ -801,10 +795,8 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy); const bool blit_stencil = device.IsExtShaderStencilExportSupported(); const BlitImagePipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .operation = operation, - .samples = dst_framebuffer->Samples(), - .target = 0, }; VkPipeline pipeline{}; VkImageView src_stencil_view = VK_NULL_HANDLE; @@ -812,7 +804,7 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, pipeline = FindOrEmplaceDepthStencilPipeline(key); src_stencil_view = src_image_view.StencilView(); } else { - pipeline = FindOrEmplaceBlitDepthPipeline(key.formats); + pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass); } BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler, src_image_view.DepthView(), src_stencil_view, blit_stencil); @@ -820,22 +812,26 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - Convert(*convert_depth_to_float_frag, dst_framebuffer, src_image_view); + ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass()); + Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - Convert(*convert_float_to_depth_frag, dst_framebuffer, src_image_view); + ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass()); + Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - Convert(*convert_depth_to_float_frag, dst_framebuffer, src_image_view); + ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass()); + Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - Convert(*convert_float_to_depth_frag, dst_framebuffer, src_image_view); + ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass()); + Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, @@ -845,37 +841,45 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping"); return; } - Convert(*convert_abgr8_to_d24s8_frag, dst_framebuffer, src_image_view); + ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(), + convert_abgr8_to_d24s8_frag); + Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { - Convert(*convert_abgr8_to_d32f_frag, dst_framebuffer, src_image_view); + ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(), + convert_abgr8_to_d32f_frag); + Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertDepthStencil(*convert_d32f_to_abgr8_frag, dst_framebuffer, src_image_view); + ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + convert_d32f_to_abgr8_frag); + ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertDepthStencil(*convert_d24s8_to_abgr8_frag, dst_framebuffer, src_image_view); + ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + convert_d24s8_to_abgr8_frag); + ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); } void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { - ConvertDepthStencil(*convert_s8d24_to_abgr8_frag, dst_framebuffer, src_image_view); + ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(), + convert_s8d24_to_abgr8_frag); + ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); } -void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u32 target, u8 color_mask, +void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask, const std::array& clear_color, const Region2D& dst_region) { const BlitImagePipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult, - .samples = dst_framebuffer->Samples(), - .target = target, }; const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key); const VkPipelineLayout layout = *clear_color_pipeline_layout; @@ -898,12 +902,11 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool f32 clear_depth, u8 stencil_mask, u32 stencil_ref, u32 stencil_compare_mask, const Region2D& dst_region) { const BlitDepthStencilPipelineKey key{ - .formats = dst_framebuffer->Formats(), + .renderpass = dst_framebuffer->RenderPass(), .depth_clear = depth_clear, .stencil_mask = stencil_mask, .stencil_compare_mask = stencil_compare_mask, .stencil_ref = stencil_ref, - .samples = dst_framebuffer->Samples(), }; const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key); const VkPipelineLayout layout = *clear_color_pipeline_layout; @@ -919,7 +922,7 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool scheduler.InvalidateState(); } -void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline pipeline, +void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, VkFormat src_vk_format, s32 scale_x, s32 scale_y, @@ -955,15 +958,17 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p .offset = dst_offset, .extent = dst_extent, }; - const VkImageView dst_view_handle = *dst_view; - RenderingAttachments attachments = - MakeRenderingAttachments(formats, std::span(&dst_view_handle, formats.num_colors), - dst_view_handle, render_area, 1); - attachments.colors[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments.depth.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - if (copy_stencil) { - attachments.stencil.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } + vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = renderpass, + .attachmentCount = 1, + .pAttachments = dst_view.address(), + .width = static_cast(dst_offset.x) + dst_extent.width, + .height = static_cast(dst_offset.y) + dst_extent.height, + .layers = 1, + }); const MSAACopyPushConstants push_constants{ .dst_offset = {dst_offset.x, dst_offset.y}, .src_offset = {copy.src_offset.x, copy.src_offset.y}, @@ -974,10 +979,10 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p src_stencil_handle = *src_stencil_view; } scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, pipeline, layout, sampler, attachments, - src_view_handle = *src_view, src_stencil_handle, src = src_image, - dst = dst_image, render_area, aspect_info, - push_constants](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, pipeline, layout, sampler, renderpass, + framebuffer_handle = *framebuffer, src_view_handle = *src_view, + src_stencil_handle, src = src_image, dst = dst_image, render_area, + aspect_info, push_constants](vk::CommandBuffer cmdbuf) { const VkImageSubresourceRange barrier_range{ .aspectMask = aspect_info.barrier_aspect, .baseMipLevel = 0, @@ -986,12 +991,10 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p .layerCount = VK_REMAINING_ARRAY_LAYERS, }; const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = aspect_info.pre_dst_stages, + .srcAccessMask = aspect_info.pre_src_access, .dstAccessMask = aspect_info.pre_src_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1000,12 +1003,10 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p .image = src, .subresourceRange = barrier_range, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = aspect_info.pre_dst_stages, + .srcAccessMask = aspect_info.pre_src_access, .dstAccessMask = aspect_info.pre_dst_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1015,8 +1016,18 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p .subresourceRange = barrier_range, }, }; - cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers); - BeginRendering(cmdbuf, attachments); + cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0, + nullptr, nullptr, pre_barriers); + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = renderpass, + .framebuffer = framebuffer_handle, + .renderArea = render_area, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); VkDescriptorSet descriptor_set = VK_NULL_HANDLE; if (src_stencil_handle != VK_NULL_HANDLE) { descriptor_set = two_textures_descriptor_allocator.Commit(); @@ -1041,14 +1052,12 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p cmdbuf.SetScissor(0, render_area); cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); - const VkImageMemoryBarrier2 post_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + cmdbuf.EndRenderPass(); + const VkImageMemoryBarrier post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = aspect_info.post_src_stages, .srcAccessMask = aspect_info.post_src_access, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = aspect_info.post_dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1056,18 +1065,21 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p .image = dst, .subresourceRange = barrier_range, }; - cmdbuf.PipelineBarrier(post_barrier); + cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0, + post_barrier); }); msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), .src_view = std::move(src_view), .dst_view = std::move(dst_view), + .framebuffer = std::move(framebuffer), }); if (copy_stencil) { msaa_copy_resources.push_back(MSAACopyResources{ .tick = scheduler.CurrentTick(), .src_view = std::move(src_stencil_view), .dst_view = vk::ImageView{}, + .framebuffer = vk::Framebuffer{}, }); } } @@ -1075,9 +1087,10 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p scheduler.InvalidateState(); } -void BlitImageHelper::CopyMSAA(VkImage dst_image, VideoCore::Surface::PixelFormat dst_format, - VkImage src_image, VideoCore::Surface::PixelFormat src_format, - u32 num_samples, std::span copies, +void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + VideoCore::Surface::PixelFormat src_format, u32 num_samples, + std::span copies, bool msaa_to_non_msaa) { const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples)); const s32 scale_x = 1 << samples_x; @@ -1086,10 +1099,14 @@ void BlitImageHelper::CopyMSAA(VkImage dst_image, VideoCore::Surface::PixelForma if (msaa_to_non_msaa) { samples = VK_SAMPLE_COUNT_1_BIT; } - const RenderingFormats formats = MakeRenderingFormats( - device, std::span(&dst_format, 1), VideoCore::Surface::PixelFormat::Invalid); + RenderPassKey renderpass_key{}; + renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); + renderpass_key.color_formats[0] = dst_format; + renderpass_key.depth_format = VideoCore::Surface::PixelFormat::Invalid; + renderpass_key.samples = samples; + const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key); const MSAACopyPipelineKey key{ - .formats = formats, + .renderpass = renderpass, .samples = samples, .msaa_to_non_msaa = msaa_to_non_msaa, .format_class = FormatClass(dst_format), @@ -1098,38 +1115,39 @@ void BlitImageHelper::CopyMSAA(VkImage dst_image, VideoCore::Surface::PixelForma .src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT, .barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT, - .pre_src_dst_access = VK_ACCESS_2_SHADER_READ_BIT, + .pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, .pre_dst_dst_access = - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, .pre_dst_stages = - VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .post_src_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - .post_src_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, }; const VkFormat src_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; const VkFormat dst_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format; - CopyMSAAImpl(formats, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout, + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout, dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, aspect_info, false); } -void BlitImageHelper::Convert(VkShaderModule module, const Framebuffer* dst_framebuffer, +void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view) { const VkPipelineLayout layout = *one_texture_pipeline_layout; - const VkPipeline pipeline = - FindOrEmplaceConvertPipeline(dst_framebuffer->Formats(), module, layout); const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); const VkSampler sampler = *nearest_sampler; const VkExtent2D extent = GetConversionExtent(src_image_view); - const Region2D region{ - .start = {0, 0}, - .end = {static_cast(extent.width), static_cast(extent.height)}, - }; RecordShaderReadBarrier(scheduler, src_image_view); - DiscardCoveredAttachments(scheduler, dst_framebuffer, region, false); scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) { const VkOffset2D offset{ @@ -1166,23 +1184,15 @@ void BlitImageHelper::Convert(VkShaderModule module, const Framebuffer* dst_fram scheduler.InvalidateState(); } -void BlitImageHelper::ConvertDepthStencil(VkShaderModule module, - const Framebuffer* dst_framebuffer, +void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, ImageView& src_image_view) { const VkPipelineLayout layout = *two_textures_pipeline_layout; - const VkPipeline pipeline = - FindOrEmplaceConvertPipeline(dst_framebuffer->Formats(), module, layout); const VkImageView src_depth_view = src_image_view.DepthView(); const VkImageView src_stencil_view = src_image_view.StencilView(); const VkSampler sampler = *nearest_sampler; const VkExtent2D extent = GetConversionExtent(src_image_view); - const Region2D region{ - .start = {0, 0}, - .end = {static_cast(extent.width), static_cast(extent.height)}, - }; RecordShaderReadBarrier(scheduler, src_image_view); - DiscardCoveredAttachments(scheduler, dst_framebuffer, region, false); scheduler.RequestRenderpass(dst_framebuffer); scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, this](vk::CommandBuffer cmdbuf) { @@ -1228,17 +1238,32 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe blit_color_keys.push_back(key); const std::array stages = MakeStages(*full_screen_vert, *blit_color_to_color_frag); - BlendAttachments blend_attachments{}; - blend_attachments[key.target] = PIPELINE_COLOR_BLEND_ATTACHMENT_STATE; + const VkPipelineColorBlendAttachmentState blend_attachment{ + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; // TODO: programmable blending - const VkPipelineColorBlendStateCreateInfo color_blend_create_info = - MakeColorBlendState(blend_attachments, key.formats); - const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples); + const VkPipelineColorBlendStateCreateInfo color_blend_create_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_CLEAR, + .attachmentCount = 1, + .pAttachments = &blend_attachment, + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1247,15 +1272,16 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe .pTessellationState = nullptr, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pMultisampleState = &multisample_ci, - .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = nullptr, .pColorBlendState = &color_blend_create_info, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *one_texture_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_color_pipelines.back(); } @@ -1266,12 +1292,10 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip } blit_depth_stencil_keys.push_back(key); const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag); - const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1280,15 +1304,16 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip .pTessellationState = nullptr, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pMultisampleState = &multisample_ci, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *two_textures_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_depth_stencil_pipelines.back(); } @@ -1310,16 +1335,20 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; - BlendAttachments blend_attachments{}; - blend_attachments[key.target] = color_blend_attachment_state; - const VkPipelineColorBlendStateCreateInfo color_blend_create_info = - MakeColorBlendState(blend_attachments, key.formats); - const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples); + const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_CLEAR, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment_state, + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1328,15 +1357,16 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel .pTessellationState = nullptr, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pMultisampleState = &multisample_ci, - .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO, - .pColorBlendState = &color_blend_create_info, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .pColorBlendState = &color_blend_state_generic_create_info, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *clear_color_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *clear_color_pipelines.back(); } @@ -1371,15 +1401,10 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .minDepthBounds = 0.0f, .maxDepthBounds = 0.0f, }; - const BlendAttachments blend_attachments{}; - const VkPipelineColorBlendStateCreateInfo color_blend_create_info = - MakeColorBlendState(blend_attachments, key.formats); - const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1388,15 +1413,16 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .pTessellationState = nullptr, .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pMultisampleState = &multisample_ci, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &depth_stencil_ci, - .pColorBlendState = &color_blend_create_info, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *clear_color_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *clear_stencil_pipelines.back(); } @@ -1419,10 +1445,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip .alphaToOneEnable = VK_FALSE, }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); blit_msaa_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1436,10 +1461,11 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *one_texture_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *blit_msaa_color_pipelines.back(); } @@ -1467,10 +1493,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( .alphaToOneEnable = VK_FALSE, }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1485,6 +1510,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, @@ -1492,18 +1518,17 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline( return *pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(const RenderingFormats& formats) { - const auto it = std::ranges::find(blit_depth_keys, formats); +VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) { + const auto it = std::ranges::find(blit_depth_keys, renderpass); if (it != blit_depth_keys.end()) { return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)]; } - blit_depth_keys.push_back(formats); + blit_depth_keys.push_back(renderpass); const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo(); blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1517,6 +1542,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(const RenderingFormat .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *one_texture_pipeline_layout, + .renderPass = renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, @@ -1524,23 +1550,22 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(const RenderingFormat return *blit_depth_pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline( - const RenderingFormats& formats, bool resolve_stencil) { +VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, + bool resolve_stencil) { auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys; auto& pipelines = resolve_stencil ? resolve_depth_stencil_pipelines : resolve_depth_pipelines; - const auto it = std::ranges::find(keys, formats); + const auto it = std::ranges::find(keys, renderpass); if (it != keys.end()) { return *pipelines[std::distance(keys.begin(), it)]; } - keys.push_back(formats); + keys.push_back(renderpass); const std::array stages = MakeStages(*full_screen_vert, resolve_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo(); pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1555,16 +1580,18 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline( .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout, + .renderPass = renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *pipelines.back(); } -void BlitImageHelper::CopyMSAADepth(VkImage dst_image, VideoCore::Surface::PixelFormat dst_format, - VkImage src_image, VideoCore::Surface::PixelFormat src_format, - u32 num_samples, std::span copies, +void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + VideoCore::Surface::PixelFormat src_format, u32 num_samples, + std::span copies, bool copy_stencil, bool msaa_to_non_msaa) { const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast(num_samples)); const s32 scale_x = 1 << samples_x; @@ -1573,9 +1600,13 @@ void BlitImageHelper::CopyMSAADepth(VkImage dst_image, VideoCore::Surface::Pixel if (msaa_to_non_msaa) { samples = VK_SAMPLE_COUNT_1_BIT; } - const RenderingFormats formats = MakeRenderingFormats(device, {}, dst_format); + RenderPassKey renderpass_key{}; + renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid); + renderpass_key.depth_format = dst_format; + renderpass_key.samples = samples; + const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key); const MSAACopyPipelineKey key{ - .formats = formats, + .renderpass = renderpass, .samples = samples, .msaa_to_non_msaa = msaa_to_non_msaa, .format_class = MSAACopyFormatClass::Float, @@ -1593,19 +1624,27 @@ void BlitImageHelper::CopyMSAADepth(VkImage dst_image, VideoCore::Surface::Pixel .src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT, .attachment_aspect = attachment_aspect, .barrier_aspect = attachment_aspect, - .pre_src_dst_access = VK_ACCESS_2_SHADER_READ_BIT, - .pre_dst_dst_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_access = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT, + .pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, .pre_dst_stages = - VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, - .post_src_access = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .post_src_stages = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, + .post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, }; const VkFormat src_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format; const VkFormat dst_vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format; - CopyMSAAImpl(formats, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout, + CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout, dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies, aspect_info, copy_stencil); } @@ -1638,10 +1677,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline .alphaToOneEnable = VK_FALSE, }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); msaa_copy_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1655,10 +1693,11 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *msaa_copy_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); + }, device.StaticPipelineCache())); return *msaa_copy_pipelines.back(); } @@ -1716,10 +1755,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPip }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo(); pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1734,6 +1772,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPip .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout : *msaa_copy_pipeline_layout, + .renderPass = key.renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, @@ -1741,32 +1780,25 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPip return *pipelines.back(); } -VkPipeline BlitImageHelper::FindOrEmplaceConvertPipeline(const RenderingFormats& formats, - VkShaderModule module, - VkPipelineLayout layout) { - const ConvertPipelineKey key{ - .formats = formats, - .module = module, - }; - const auto it = std::ranges::find(convert_keys, key); - if (it != convert_keys.end()) { - return *convert_pipelines[std::distance(convert_keys.begin(), it)]; +void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { + ConvertPipeline(pipeline, renderpass, false); +} + +void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { + ConvertPipeline(pipeline, renderpass, true); +} + +void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module, bool single_texture, + bool is_target_depth) { + if (pipeline) { + return; } - convert_keys.push_back(key); - const VkPipelineDepthStencilStateCreateInfo* depth_stencil_ci = nullptr; - const VkPipelineColorBlendStateCreateInfo* color_blend_ci = - &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO; - if (formats.num_colors == 0) { - depth_stencil_ci = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - color_blend_ci = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO; - } - const std::array stages = MakeStages(*full_screen_vert, module); - const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = - GetPipelineInputAssemblyStateCreateInfo(device); - const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo(); - convert_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ + const std::array stages = MakeStages(*full_screen_vert, *module); + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -1776,15 +1808,59 @@ VkPipeline BlitImageHelper::FindOrEmplaceConvertPipeline(const RenderingFormats& .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pDepthStencilState = depth_stencil_ci, - .pColorBlendState = color_blend_ci, + .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr, + .pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO + : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .layout = layout, + .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout, + .renderPass = renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, - })); - return *convert_pipelines.back(); + }, device.StaticPipelineCache()); +} + +void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module) { + ConvertPipelineEx(pipeline, renderpass, module, false, false); +} + +void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module) { + ConvertPipelineEx(pipeline, renderpass, module, true, true); +} + +void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, + bool is_target_depth) { + if (pipeline) { + return; + } + VkShaderModule frag_shader = + is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag; + const std::array stages = MakeStages(*full_screen_vert, frag_shader); + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(stages.size()), + .pStages = stages.data(), + .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr, + .pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO + : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, + .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .layout = *one_texture_pipeline_layout, + .renderPass = renderpass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = 0, + }, device.StaticPipelineCache()); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index bc1db246ff..62e6743a23 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -11,7 +11,6 @@ #include "video_core/engines/fermi_2d.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" -#include "video_core/renderer_vulkan/vk_render_pass_cache.h" #include "video_core/surface.h" #include "video_core/texture_cache/types.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -25,34 +24,25 @@ using VideoCommon::Region2D; class Device; class Framebuffer; class ImageView; +class RenderPassCache; class StateTracker; class Scheduler; struct BlitImagePipelineKey { constexpr auto operator<=>(const BlitImagePipelineKey&) const noexcept = default; - RenderingFormats formats; + VkRenderPass renderpass; Tegra::Engines::Fermi2D::Operation operation; - VkSampleCountFlagBits samples; - u32 target; }; struct BlitDepthStencilPipelineKey { constexpr auto operator<=>(const BlitDepthStencilPipelineKey&) const noexcept = default; - RenderingFormats formats; + VkRenderPass renderpass; bool depth_clear; u8 stencil_mask; u32 stencil_compare_mask; u32 stencil_ref; - VkSampleCountFlagBits samples; -}; - -struct ConvertPipelineKey { - constexpr auto operator<=>(const ConvertPipelineKey&) const noexcept = default; - - RenderingFormats formats; - VkShaderModule module; }; enum class MSAACopyFormatClass : u32 { @@ -64,7 +54,7 @@ enum class MSAACopyFormatClass : u32 { struct MSAACopyPipelineKey { constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default; - RenderingFormats formats; + VkRenderPass renderpass; VkSampleCountFlagBits samples; bool msaa_to_non_msaa; MSAACopyFormatClass format_class; @@ -73,7 +63,7 @@ struct MSAACopyPipelineKey { struct BlitMSAAPipelineKey { constexpr auto operator<=>(const BlitMSAAPipelineKey&) const noexcept = default; - RenderingFormats formats; + VkRenderPass renderpass; VkSampleCountFlagBits samples; }; @@ -127,32 +117,38 @@ public: void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); - void ClearColor(const Framebuffer* dst_framebuffer, u32 target, u8 color_mask, + void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask, const std::array& clear_color, const Region2D& dst_region); void ClearDepthStencil(const Framebuffer* dst_framebuffer, bool depth_clear, f32 clear_depth, u8 stencil_mask, u32 stencil_ref, u32 stencil_compare_mask, const Region2D& dst_region); - void CopyMSAA(VkImage dst_image, VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + void CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, VideoCore::Surface::PixelFormat src_format, u32 num_samples, std::span copies, bool msaa_to_non_msaa); - void CopyMSAADepth(VkImage dst_image, VideoCore::Surface::PixelFormat dst_format, - VkImage src_image, VideoCore::Surface::PixelFormat src_format, - u32 num_samples, std::span copies, - bool copy_stencil, bool msaa_to_non_msaa); + void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image, + VideoCore::Surface::PixelFormat dst_format, VkImage src_image, + VideoCore::Surface::PixelFormat src_format, u32 num_samples, + std::span copies, bool copy_stencil, + bool msaa_to_non_msaa); private: struct MSAACopyAspectInfo { VkImageAspectFlags src_view_aspect; VkImageAspectFlags attachment_aspect; VkImageAspectFlags barrier_aspect; - VkAccessFlags2 pre_src_dst_access; - VkAccessFlags2 pre_dst_dst_access; - VkPipelineStageFlags2 pre_dst_stages; - VkAccessFlags2 post_src_access; - VkPipelineStageFlags2 post_src_stages; + VkAccessFlags pre_src_access; + VkAccessFlags pre_src_dst_access; + VkAccessFlags pre_dst_dst_access; + VkPipelineStageFlags pre_src_stages; + VkPipelineStageFlags pre_dst_stages; + VkAccessFlags post_src_access; + VkAccessFlags post_dst_access; + VkPipelineStageFlags post_src_stages; + VkPipelineStageFlags post_dst_stages; }; void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, @@ -160,16 +156,16 @@ private: VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view, bool blit_stencil); - void CopyMSAAImpl(const RenderingFormats& formats, VkPipeline pipeline, VkPipelineLayout layout, + void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout, VkImage dst_image, VkFormat dst_vk_format, VkImage src_image, VkFormat src_vk_format, s32 scale_x, s32 scale_y, std::span copies, const MSAACopyAspectInfo& aspect_info, bool copy_stencil); - void Convert(VkShaderModule module, const Framebuffer* dst_framebuffer, + void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, const ImageView& src_image_view); - void ConvertDepthStencil(VkShaderModule module, const Framebuffer* dst_framebuffer, + void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, ImageView& src_image_view); [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); @@ -186,12 +182,24 @@ private: [[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline( const BlitMSAAPipelineKey& key, bool blit_stencil); - [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(const RenderingFormats& formats); - [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline( - const RenderingFormats& formats, bool resolve_stencil); - [[nodiscard]] VkPipeline FindOrEmplaceConvertPipeline(const RenderingFormats& formats, - VkShaderModule module, - VkPipelineLayout layout); + [[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass); + [[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass, + bool resolve_stencil); + + void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); + + void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); + + void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); + + void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module, bool single_texture, bool is_target_depth); + + void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module); + + void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, + vk::ShaderModule& module); const Device& device; Scheduler& scheduler; @@ -252,24 +260,32 @@ private: std::vector msaa_copy_depth_stencil_pipelines; std::vector blit_msaa_color_keys; std::vector blit_msaa_color_pipelines; - std::vector blit_depth_keys; + std::vector blit_depth_keys; std::vector blit_depth_pipelines; std::vector blit_msaa_depth_keys; std::vector blit_msaa_depth_pipelines; std::vector blit_msaa_depth_stencil_keys; std::vector blit_msaa_depth_stencil_pipelines; - std::vector resolve_depth_keys; + std::vector resolve_depth_keys; std::vector resolve_depth_pipelines; - std::vector resolve_depth_stencil_keys; + std::vector resolve_depth_stencil_keys; std::vector resolve_depth_stencil_pipelines; struct MSAACopyResources { u64 tick; vk::ImageView src_view; vk::ImageView dst_view; + vk::Framebuffer framebuffer; }; std::deque msaa_copy_resources; - std::vector convert_keys; - std::vector convert_pipelines; + vk::Pipeline convert_d32_to_r32_pipeline; + vk::Pipeline convert_r32_to_d32_pipeline; + vk::Pipeline convert_d16_to_r16_pipeline; + vk::Pipeline convert_r16_to_d16_pipeline; + vk::Pipeline convert_abgr8_to_d24s8_pipeline; + vk::Pipeline convert_abgr8_to_d32f_pipeline; + vk::Pipeline convert_d32f_to_abgr8_pipeline; + vk::Pipeline convert_d24s8_to_abgr8_pipeline; + vk::Pipeline convert_s8d24_to_abgr8_pipeline; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 9a2dc76202..0ced890929 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -88,18 +88,6 @@ bool ComputeAttachment0DualSourceBlend(const Maxwell& regs) { : uses_dual_source(regs.blend); } -bool WritesColorTarget(const Maxwell& regs, size_t index, bool static_masks) { - if (!static_masks || (index == 0 && regs.anti_alias_alpha_control.alpha_to_coverage != 0)) { - return true; - } - size_t mask_index = index; - if (regs.color_mask_common) { - mask_index = 0; - } - const auto& mask = regs.color_mask[mask_index]; - return mask.R || mask.G || mask.B || mask.A; -} - void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), [](const auto& layout) { @@ -113,8 +101,7 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& } } // Anonymous namespace -void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features, - u32 color_outputs) { +void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) { const Maxwell& regs = maxwell3d.regs; const auto topology_ = maxwell3d.draw_manager.draw_state.topology; @@ -132,14 +119,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe xfb_enabled.Assign(regs.transform_feedback_enabled != 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); - if (topology_ == Maxwell::PrimitiveTopology::Patches) { - tessellation_primitive.Assign( - static_cast(regs.tessellation.params.domain_type.Value())); - tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); - tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == - Maxwell::Tessellation::OutputPrimitives::Triangles_CW); - patch_control_points_minus_one.Assign(regs.patch_vertices - 1); - } + tessellation_primitive.Assign(static_cast(regs.tessellation.params.domain_type.Value())); + tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); + tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == + Maxwell::Tessellation::OutputPrimitives::Triangles_CW); + patch_control_points_minus_one.Assign(regs.patch_vertices - 1); const bool can_collapse_topology_class = features.has_extended_dynamic_state && features.has_extended_dynamic_state_2; topology.Assign(can_collapse_topology_class @@ -154,13 +138,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); - const bool drop_unused = features.has_dynamic_rendering_unused_attachments; - const bool depth_used = regs.depth_test_enable || regs.depth_bounds_enable || - regs.stencil_enable || extended_dynamic_state; - if (regs.zeta_enable != 0 && regs.zeta.Address() != 0 && (depth_used || !drop_unused)) { - depth_enabled.Assign(1); - depth_format.Assign(static_cast(regs.zeta.format)); - } + depth_enabled.Assign(regs.zeta_enable != 0 ? 1 : 0); + depth_format.Assign(static_cast(regs.zeta.format)); y_negate.Assign(regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft ? 1 : 0); bool use_last_provoking_vertex = false; @@ -189,39 +168,19 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + app_stage.Assign(maxwell3d.engine_state); - line_stipple_factor = 0; - line_stipple_pattern = 0; - if (regs.line_stipple_enable || features.has_dynamic_state3_line_stipple_enable) { - line_stipple_factor = regs.line_stipple_params.factor; - line_stipple_pattern = regs.line_stipple_params.pattern; - } + depth_bounds_min = static_cast(regs.depth_bounds[0]); + depth_bounds_max = static_cast(regs.depth_bounds[1]); - const bool static_masks = - !features.has_extended_dynamic_state_3_blend && !features.has_color_write_enable; - const auto previous_formats = color_formats; - color_formats.fill(static_cast(Tegra::RenderTargetFormat::NONE)); - const size_t num_targets = (std::min)(size_t{regs.rt_control.count}, color_formats.size()); - size_t num_formats = 0; - for (size_t index = 0; index < num_targets; ++index) { - const bool used = ((color_outputs >> index) & 1) != 0 && - WritesColorTarget(regs, index, static_masks); - if (regs.rt[index].Address() != 0 && (used || !drop_unused)) { - num_formats = index + 1; - } - } - for (size_t index = 0; index < num_formats; ++index) { - color_formats[index] = static_cast(regs.rt[index].format); - } - alpha_test_ref = 0; - if (regs.alpha_test_enabled != 0) { - alpha_test_ref = std::bit_cast(regs.alpha_test_ref); - } - point_size = 0; - if (topology_ == Maxwell::PrimitiveTopology::Points || - regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)) { - point_size = std::bit_cast(regs.point_size); + line_stipple_factor = regs.line_stipple_params.factor; + line_stipple_pattern = regs.line_stipple_params.pattern; + + for (size_t i = 0; i < regs.rt.size(); ++i) { + color_formats[i] = static_cast(regs.rt[i].format); } + alpha_test_ref = std::bit_cast(regs.alpha_test_ref); + point_size = std::bit_cast(regs.point_size); if (maxwell3d.dirty.flags[Dirty::VertexInput]) { if (features.has_dynamic_vertex_input) { @@ -282,15 +241,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } if (!extended_dynamic_state_3_blend) { - if (maxwell3d.dirty.flags[Dirty::Blending] || color_formats != previous_formats) { + if (maxwell3d.dirty.flags[Dirty::Blending]) { maxwell3d.dirty.flags[Dirty::Blending] = false; for (size_t index = 0; index < attachments.size(); ++index) { + attachments[index].Refresh(regs, index); auto& attachment = attachments[index]; - attachment.raw = 0; - if (color_formats[index] == static_cast(Tegra::RenderTargetFormat::NONE)) { - continue; - } - attachment.Refresh(regs, index); if (color_write_enable_dynamic && attachment.mask_r == 0 && attachment.mask_g == 0 && attachment.mask_b == 0 && attachment.mask_a == 0) { @@ -359,34 +314,28 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { packed_front_face = 1 - packed_front_face; } - if (regs.stencil_enable) { - front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); - front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); - front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); - front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func)); - if (regs.stencil_two_side_enable) { - back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail)); - back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail)); - back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass)); - back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func)); - } else { - back.action_stencil_fail.Assign(front.action_stencil_fail); - back.action_depth_fail.Assign(front.action_depth_fail); - back.action_depth_pass.Assign(front.action_depth_pass); - back.test_func.Assign(front.test_func); - } + front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op.fail)); + front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op.zfail)); + front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op.zpass)); + front.test_func.Assign(PackComparisonOp(regs.stencil_front_op.func)); + if (regs.stencil_two_side_enable) { + back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op.fail)); + back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op.zfail)); + back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op.zpass)); + back.test_func.Assign(PackComparisonOp(regs.stencil_back_op.func)); + } else { + back.action_stencil_fail.Assign(front.action_stencil_fail); + back.action_depth_fail.Assign(front.action_depth_fail); + back.action_depth_pass.Assign(front.action_depth_pass); + back.test_func.Assign(front.test_func); } stencil_enable.Assign(regs.stencil_enable); depth_write_enable.Assign(regs.depth_write_enabled); depth_bounds_enable.Assign(regs.depth_bounds_enable); depth_test_enable.Assign(regs.depth_test_enable); front_face.Assign(packed_front_face); - if (regs.depth_test_enable) { - depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); - } - if (regs.gl_cull_test_enabled) { - cull_face.Assign(PackCullFace(regs.gl_cull_face)); - } + depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); + cull_face.Assign(PackCullFace(regs.gl_cull_face)); cull_enable.Assign(regs.gl_cull_test_enabled != 0 ? 1 : 0); } @@ -415,9 +364,6 @@ void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs, const DynamicFeatures& features) { if (!features.has_dynamic_state3_logic_op_enable) { logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - if (!regs.logic_op.enable) { - logic_op.Assign(0); - } } if (!features.has_dynamic_state3_depth_clamp_enable) { depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 6e114ec111..36a4b4b7a2 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -38,7 +38,6 @@ struct DynamicFeatures { bool has_provoking_vertex_first_mode; bool has_provoking_vertex_last_mode; bool has_provoking_vertex_tf_preserve; - bool has_dynamic_rendering_unused_attachments; }; struct FixedPipelineState { @@ -231,6 +230,7 @@ struct FixedPipelineState { BitField<14, 1, u32> smooth_lines; BitField<15, 1, u32> alpha_to_coverage_enabled; BitField<16, 1, u32> alpha_to_one_enabled; + BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; }; std::array color_formats; @@ -238,8 +238,6 @@ struct FixedPipelineState { u32 driver_version; u32 alpha_test_ref; u32 point_size; - u32 line_stipple_factor; - u32 line_stipple_pattern; std::array viewport_swizzles; union { @@ -256,8 +254,13 @@ struct FixedPipelineState { VideoCommon::TransformFeedbackState xfb_state; - void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features, - u32 color_outputs); + u32 depth_bounds_min; + u32 depth_bounds_max; + + u32 line_stipple_factor; + u32 line_stipple_pattern; + + void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features); size_t Hash() const noexcept; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 12c311ad35..6c18ade926 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging.h" +#include "common/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/surface.h" @@ -70,14 +71,12 @@ VkSamplerAddressMode WrapMode(const Device& device, } ASSERT(false); return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceBorder: UNIMPLEMENTED(); - [[fallthrough]]; - case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceClampOGL: - if (!device.IsKhrSamplerMirrorClampToEdgeSupported()) { - return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - } return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; default: UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", wrap_mode); @@ -224,6 +223,10 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_UNORM_BLOCK, 0, EAC_R11_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_SNORM_BLOCK, 0, EAC_R11_SNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_UNORM_BLOCK, 0, EAC_R11G11_UNORM) \ + SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_SNORM_BLOCK, 0, EAC_R11G11_SNORM) \ /* Depth formats */ \ SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \ SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \ @@ -243,11 +246,21 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with bool const is_srgb = with_srgb && VideoCore::Surface::IsPixelFormatSRGB(pixel_format); // Transcode on hardware that doesn't support ASTC natively if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) { - if (is_srgb) { - tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32; - } else { - tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; - tuple.usage |= usage_storage; + switch (Settings::values.astc_recompression.GetValue()) { + case Settings::AstcRecompression::Uncompressed: + if (is_srgb) { + tuple.format = VK_FORMAT_A8B8G8R8_SRGB_PACK32; + } else { + tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + tuple.usage |= usage_storage; + } + break; + case Settings::AstcRecompression::Bc1: + tuple.format = is_srgb ? VK_FORMAT_BC1_RGBA_SRGB_BLOCK : VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + break; + case Settings::AstcRecompression::Bc3: + tuple.format = is_srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK; + break; } } if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) { @@ -269,7 +282,17 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with } } else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) { // Transcode on hardware that doesn't support ETC2 natively - tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + if (pixel_format == PixelFormat::EAC_R11_SNORM) { + tuple.format = VK_FORMAT_R8_SNORM; + } else if (pixel_format == PixelFormat::EAC_R11_UNORM) { + tuple.format = VK_FORMAT_R8_UNORM; + } else if (pixel_format == PixelFormat::EAC_R11G11_SNORM) { + tuple.format = VK_FORMAT_R8G8_SNORM; + } else if (pixel_format == PixelFormat::EAC_R11G11_UNORM) { + tuple.format = VK_FORMAT_R8G8_UNORM; + } else { + tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + } } bool const attachable = (tuple.usage & usage_attachable) != 0; bool const storage = (tuple.usage & usage_storage) != 0; diff --git a/src/video_core/renderer_vulkan/present/frame_gen.cpp b/src/video_core/renderer_vulkan/present/frame_gen.cpp index 186562a6e7..e6d857323b 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.cpp +++ b/src/video_core/renderer_vulkan/present/frame_gen.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include -#include #include #include @@ -10,12 +9,10 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/settings.h" -#include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h" -#include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/renderer_vulkan/present/frame_gen.h" #include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/vulkan_common/vulkan_device.h" namespace Vulkan { @@ -26,13 +23,27 @@ constexpr size_t COLOR_CHANNELS = 4; constexpr u64 LSFG_REQUIRED_FRAMES = 2; constexpr u32 LSFG_RECURRENCE_FRAMES = 2; -[[nodiscard]] f32 ConfiguredFlowScale() { - if (Settings::values.frame_gen_flow_scale_auto.GetValue()) { - return std::clamp(1.0f / Settings::values.resolution_info.up_factor, 0.25f, 1.0f); - } +[[nodiscard]] f32 ManualFlowScale() { return static_cast(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f; } +[[nodiscard]] f32 ConfiguredFlowScale(VkExtent2D guest_extent, VkExtent2D presented_extent) { + if (!Settings::values.frame_gen_flow_scale_auto.GetValue()) { + return ManualFlowScale(); + } + if (guest_extent.width == 0 || presented_extent.width == 0) { + return 1.0f; + } + + const f32 rendered_width = static_cast(guest_extent.width) * + Settings::values.resolution_info.up_factor; + const f32 ratio = rendered_width / static_cast(presented_extent.width); + + constexpr f32 FLOW_SCALE_STEPS = 20.0f; + const f32 stepped = std::ceil(ratio * FLOW_SCALE_STEPS) / FLOW_SCALE_STEPS; + return std::clamp(stepped, 0.25f, 1.0f); +} + bool IsBlueFirst(VkFormat format) { return format == VK_FORMAT_B8G8R8A8_UNORM || format == VK_FORMAT_B8G8R8A8_SRGB; } @@ -69,7 +80,7 @@ void WritePortablePixmap(const std::filesystem::path& path, const std::string& m void WriteGrayscalePgm(const std::filesystem::path& path, VkExtent2D extent, std::span pixels) { const size_t expected = static_cast(extent.width) * extent.height; - WritePortablePixmap(path, "P5", extent, pixels.subspan(0, (std::min)(expected, pixels.size()))); + WritePortablePixmap(path, "P5", extent, pixels.subspan(0, std::min(expected, pixels.size()))); } void WriteRaw(const std::filesystem::path& path, std::span pixels) { @@ -102,17 +113,13 @@ void WriteColorPpm(const std::filesystem::path& path, VkExtent2D extent, WritePortablePixmap(path, "P6", extent, rgb); } -VkImageMemoryBarrier2 MakeTransitionBarrier(VkImage image, VkPipelineStageFlags2 src_stage, - VkAccessFlags2 src_access, - VkPipelineStageFlags2 dst_stage, - VkAccessFlags2 dst_access, VkImageLayout old_layout, - VkImageLayout new_layout) { - return VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, +VkImageMemoryBarrier MakeTransitionBarrier(VkImage image, VkAccessFlags src_access, + VkAccessFlags dst_access, VkImageLayout old_layout, + VkImageLayout new_layout) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = src_stage, .srcAccessMask = src_access, - .dstStageMask = dst_stage, .dstAccessMask = dst_access, .oldLayout = old_layout, .newLayout = new_layout, @@ -129,63 +136,55 @@ VkImageMemoryBarrier2 MakeTransitionBarrier(VkImage image, VkPipelineStageFlags2 }; } -void DrawSourceFrame(vk::CommandBuffer cmdbuf, VkPipeline pipeline, VkPipelineLayout layout, - VkDescriptorSet set, LsfgImage& destination, VkExtent2D extent) { - const VkImageMemoryBarrier2 before = MakeTransitionBarrier( - destination.Handle(), VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_NONE, - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - destination.Layout(), VK_IMAGE_LAYOUT_GENERAL); - cmdbuf.PipelineBarrier(before); - - const VkViewport viewport{ - .x = 0.0f, - .y = 0.0f, - .width = static_cast(extent.width), - .height = static_cast(extent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, +VkImageCopy MakeCopyRegion(VkExtent2D extent) { + return VkImageCopy{ + .srcSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = {}, + .dstSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffset = {}, + .extent = {.width = extent.width, .height = extent.height, .depth = 1}, }; - const VkRect2D scissor{ - .offset = {0, 0}, - .extent = extent, - }; - BeginRendering(cmdbuf, destination.View(), extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, set, {}); - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, scissor); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); - - const VkImageMemoryBarrier2 after = MakeTransitionBarrier( - destination.Handle(), VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); - cmdbuf.PipelineBarrier(after); - - destination.SetLayout(VK_IMAGE_LAYOUT_GENERAL); } -void UpdateSourceSet(const Device& device, VkDescriptorSet set, VkSampler sampler, - VkImageView view) { - const VkDescriptorImageInfo image_info{ - .sampler = sampler, - .imageView = view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, +void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& destination, + VkExtent2D extent) { + const auto make_barrier = MakeTransitionBarrier; + + const std::array before{ + make_barrier(source, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), + make_barrier(destination.Handle(), VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + destination.Layout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), }; - const VkWriteDescriptorSet write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = set, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before); + + cmdbuf.CopyImage(source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destination.Handle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeCopyRegion(extent)); + + const std::array after{ + make_barrier(source, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL), + make_barrier(destination.Handle(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL), }; - device.GetLogical().UpdateDescriptorSets(std::array{write}, {}); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, {}, {}, after); + + destination.SetLayout(VK_IMAGE_LAYOUT_GENERAL); } } // Anonymous namespace @@ -195,19 +194,18 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_) FrameGen::~FrameGen() = default; -void FrameGen::Process(const Device& device, VkImageView source, VkExtent2D extent) { +void FrameGen::Process(const Device& device, Frame* frame, VkFormat format, + VkExtent2D guest_extent) { generated = false; if (!shaders) { shaders.emplace(device); - CreateInputPass(device); } if (unavailable || !Settings::values.frame_gen.GetValue()) { if (chain) { scheduler.Finish(); chain.reset(); - outputs = {}; } warm_streak = 0; return; @@ -218,39 +216,37 @@ void FrameGen::Process(const Device& device, VkImageView source, VkExtent2D exte return; } - if (source == VK_NULL_HANDLE) { + if (!frame->storage_view) { warm_streak = 0; return; } - const f32 flow_scale = ConfiguredFlowScale(); - const size_t generations = Settings::FrameGenMaxGenerations(); + peak_guest_extent.width = std::max(peak_guest_extent.width, guest_extent.width); + peak_guest_extent.height = std::max(peak_guest_extent.height, guest_extent.height); + + const VkExtent2D extent{.width = frame->width, .height = frame->height}; + const f32 flow_scale = ConfiguredFlowScale(peak_guest_extent, extent); if (!chain || built_extent.width != extent.width || built_extent.height != extent.height || - built_flow_scale != flow_scale || built_generations != generations) { - Rebuild(device, extent, flow_scale, generations); + built_format != format || built_flow_scale != flow_scale) { + Rebuild(device, extent, format, flow_scale); } const u64 count = frame_count++; last_count = count; - last_generations = (std::min)(plan.generations, built_generations); + last_generations = plan.generations; - const size_t input_slot = count % INPUT_SLOTS; - const bool warm = plan.warm && count + 1 >= LSFG_REQUIRED_FRAMES && - scheduler.IsFree(input_ticks[input_slot]); + const bool warm = plan.warm && count + 1 >= LSFG_REQUIRED_FRAMES; warm_streak = warm ? warm_streak + 1 : 0; generated = warm && warm_streak >= LSFG_RECURRENCE_FRAMES && plan.generations > 0; - if (warm) { - const VkDescriptorSet set = input_sets[input_slot]; - input_ticks[input_slot] = scheduler.CurrentTick(); - UpdateSourceSet(device, set, *input_sampler, source); - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, set, extent, count](vk::CommandBuffer cmdbuf) { - DrawSourceFrame(cmdbuf, *input_pipeline, *input_layout, set, chain->Input(count), - extent); + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, source = *frame->image, extent, count, + dispatch = warm](vk::CommandBuffer cmdbuf) { + CopyPresentedFrame(cmdbuf, source, chain->Input(count), extent); + if (dispatch) { chain->DispatchShared(cmdbuf, count); - }); - } + } + }); const bool dump_requested = generated && Settings::values.frame_gen_dump_flow.GetValue(); if (!dump_requested) { @@ -274,53 +270,30 @@ size_t FrameGen::GeneratedFrameCount() const { return generated ? last_generations : 0; } -std::chrono::nanoseconds FrameGen::PaceStep() const { - const f32 step = plan.interval / static_cast(last_generations + 1); - return std::chrono::duration_cast(std::chrono::duration(step)); -} +void FrameGen::GenerateInto(const Device& device, Frame* destination, size_t generation) { + chain->SetTarget(device, last_generations, generation, destination->index, + *destination->storage_view); -const LsfgImage& FrameGen::Generate(const Device& device, size_t generation) { - LsfgImage& output = outputs[generation]; - chain->SetTarget(device, last_generations, generation, output.View()); + const VkExtent2D extent{.width = destination->width, .height = destination->height}; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, count = last_count, generation_count = last_generations, generation, - image = output.Handle(), extent = built_extent](vk::CommandBuffer cmdbuf) { - chain->DispatchGeneration(cmdbuf, count, generation_count, generation, image, extent); + target = destination->index, image = *destination->image, + extent](vk::CommandBuffer cmdbuf) { + chain->DispatchGeneration(cmdbuf, count, generation_count, generation, target, image, + extent); }); - return output; } -void FrameGen::CreateInputPass(const Device& device) { - input_set_layout = - CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); - input_layout = CreateWrappedPipelineLayout(device, input_set_layout); - input_pool = CreateWrappedDescriptorPool(device, INPUT_SLOTS, INPUT_SLOTS); - std::array layouts; - layouts.fill(*input_set_layout); - input_sets = CreateWrappedDescriptorSets(input_pool, layouts); - input_vertex_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_VERT_SPV); - input_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); - input_pipeline = CreateWrappedPipeline(device, LSFG_DEFAULT_FORMAT, input_layout, - std::tie(input_vertex_shader, input_fragment_shader)); - input_sampler = CreateNearestNeighborSampler(device); -} - -void FrameGen::Rebuild(const Device& device, VkExtent2D extent, f32 flow_scale, - size_t generations) { +void FrameGen::Rebuild(const Device& device, VkExtent2D extent, VkFormat format, f32 flow_scale) { scheduler.Finish(); chain.reset(); built_flow_scale = flow_scale; - chain.emplace(device, memory_allocator, *shaders, extent, LSFG_DEFAULT_FORMAT, - built_flow_scale); - outputs = {}; - for (LsfgImage& output : std::span{outputs}.first(generations)) { - output = LsfgImage(device, memory_allocator, extent, LSFG_DEFAULT_FORMAT); - } + chain.emplace(device, memory_allocator, *shaders, extent, format, built_flow_scale); built_extent = extent; - built_generations = generations; + built_format = format; frame_count = 0; warm_streak = 0; generated = false; diff --git a/src/video_core/renderer_vulkan/present/frame_gen.h b/src/video_core/renderer_vulkan/present/frame_gen.h index 51cc142277..19c6d04289 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.h +++ b/src/video_core/renderer_vulkan/present/frame_gen.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include "common/common_types.h" @@ -16,27 +15,23 @@ namespace Vulkan { class Device; class Scheduler; +struct Frame; class FrameGen { public: explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler); ~FrameGen(); - void Process(const Device& device, VkImageView source, VkExtent2D extent); + void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent); [[nodiscard]] size_t WantedGenerations(size_t capacity); [[nodiscard]] size_t GeneratedFrameCount() const; - [[nodiscard]] std::chrono::nanoseconds PaceStep() const; - - [[nodiscard]] const LsfgImage& Generate(const Device& device, size_t generation); + void GenerateInto(const Device& device, Frame* destination, size_t generation); private: - static constexpr size_t INPUT_SLOTS = 4; - - void CreateInputPass(const Device& device); - void Rebuild(const Device& device, VkExtent2D extent, f32 flow_scale, size_t generations); + void Rebuild(const Device& device, VkExtent2D extent, VkFormat format, f32 flow_scale); void DumpDebugImages(u64 count); MemoryAllocator& memory_allocator; @@ -44,21 +39,12 @@ private: std::optional shaders; std::optional chain; - std::array outputs; - vk::DescriptorSetLayout input_set_layout; - vk::PipelineLayout input_layout; - vk::DescriptorPool input_pool; - vk::DescriptorSets input_sets; - vk::ShaderModule input_vertex_shader; - vk::ShaderModule input_fragment_shader; - vk::Pipeline input_pipeline; - vk::Sampler input_sampler; - std::array input_ticks{}; FrameGenPacer pacer; FrameGenPlan plan{}; + VkExtent2D peak_guest_extent{}; VkExtent2D built_extent{}; + VkFormat built_format{VK_FORMAT_UNDEFINED}; f32 built_flow_scale{}; - size_t built_generations{}; u64 frame_count{}; u64 last_count{}; size_t last_generations{}; diff --git a/src/video_core/renderer_vulkan/present/frame_gen_pacer.cpp b/src/video_core/renderer_vulkan/present/frame_gen_pacer.cpp index 195a81be19..78a8c83b5a 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen_pacer.cpp +++ b/src/video_core/renderer_vulkan/present/frame_gen_pacer.cpp @@ -47,7 +47,7 @@ constexpr auto PROBE_STEP_DELAY = std::chrono::milliseconds(250); } // Anonymous namespace FrameGenPlan FrameGenPacer::Plan(size_t capacity) { - const size_t ceiling = (std::min)(capacity, Settings::FrameGenMaxGenerations()); + const size_t ceiling = std::min(capacity, Settings::FrameGenMaxGenerations()); if (ceiling == 0) { Reset(); return {}; @@ -74,7 +74,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) { if (smoothed_interval > 0.0f) { f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval; if (target_rate > 0.0f) { - burst_threshold = (std::max)(burst_threshold, target_rate * BURST_TARGET_RATIO); + burst_threshold = std::max(burst_threshold, target_rate * BURST_TARGET_RATIO); } if (1.0f / interval_seconds > burst_threshold) { DeferEvaluations(interval); @@ -109,15 +109,15 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) { } if (target_rate == 0.0f) { - limit = (std::min)(Settings::FrameGenGenerations(), ceiling); + limit = std::min(Settings::FrameGenGenerations(), ceiling); output_credit = 0.0f; issued_generations = limit; - return {.generations = limit, .warm = limit > 0, .interval = smoothed_interval}; + return {.generations = limit, .warm = limit > 0}; } UpdateLimit(now, 1.0f / smoothed_interval, target_rate, ceiling); - const size_t allowed = (std::min)(limit, ceiling); + const size_t allowed = std::min(limit, ceiling); const f32 desired_outputs = smoothed_interval * target_rate; if (allowed == 0 || desired_outputs <= 1.0f) { output_credit = 0.0f; @@ -127,7 +127,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) { output_credit += desired_outputs; const size_t outputs = std::max(1, static_cast(std::floor(output_credit + CREDIT_EPSILON))); - const size_t generations = (std::min)(outputs - 1, allowed); + const size_t generations = std::min(outputs - 1, allowed); output_credit -= static_cast(generations + 1); if (output_credit < 0.0f) { @@ -137,12 +137,12 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) { } issued_generations = generations; - return {.generations = generations, .warm = true, .interval = smoothed_interval}; + return {.generations = generations, .warm = true}; } void FrameGenPacer::UpdateLimit(Clock::time_point now, f32 base_rate, f32 target_rate, size_t ceiling) { - limit = (std::min)(limit, ceiling); + limit = std::min(limit, ceiling); if (probe_until) { if (now < *probe_until) { @@ -152,9 +152,9 @@ void FrameGenPacer::UpdateLimit(Clock::time_point now, f32 base_rate, f32 target output_credit = 0.0f; const f32 previous_output = - (std::min)(target_rate, probe_base_rate * static_cast(probe_previous_limit + 1)); + std::min(target_rate, probe_base_rate * static_cast(probe_previous_limit + 1)); const f32 current_output = - (std::min)(target_rate, base_rate * static_cast(limit + 1)); + std::min(target_rate, base_rate * static_cast(limit + 1)); const bool throughput_regressed = current_output < previous_output * PROBE_THROUGHPUT_TOLERANCE; @@ -166,7 +166,7 @@ void FrameGenPacer::UpdateLimit(Clock::time_point now, f32 base_rate, f32 target if (throughput_regressed || collapsed_for_marginal_gain || emulation_slowed) { limit = probe_previous_limit; - probe_failures = (std::min)(probe_failures + 1, MAX_PROBE_FAILURES); + probe_failures = std::min(probe_failures + 1, MAX_PROBE_FAILURES); next_probe = now + ProbeBackoff(probe_failures); deficit_since.reset(); return; diff --git a/src/video_core/renderer_vulkan/present/frame_gen_pacer.h b/src/video_core/renderer_vulkan/present/frame_gen_pacer.h index 8c513af71c..7f4b23920c 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen_pacer.h +++ b/src/video_core/renderer_vulkan/present/frame_gen_pacer.h @@ -13,7 +13,6 @@ namespace Vulkan { struct FrameGenPlan { size_t generations{}; bool warm{}; - f32 interval{}; }; class FrameGenPacer { diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp index bb3138e7e6..894fb144f0 100644 --- a/src/video_core/renderer_vulkan/present/fsr.cpp +++ b/src/video_core/renderer_vulkan/present/fsr.cpp @@ -31,6 +31,7 @@ FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_c , m_extent{extent} { CreateImages(device); + CreateRenderPasses(device); CreateSampler(device); CreateShaders(device); CreateDescriptorPool(device); @@ -41,9 +42,20 @@ FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_c } void FSR::CreateImages(const Device& device) { - for (size_t stage = 0; stage < MaxFsrStage; ++stage) { - m_images[stage] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - m_image_views[stage] = CreateWrappedImageView(device, m_images[stage], VK_FORMAT_R16G16B16A16_SFLOAT); + m_dynamic_images.resize(m_image_count); + for (auto& images : m_dynamic_images) { + images.images[Easu] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Rcas] = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Easu] = CreateWrappedImageView(device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Rcas] = CreateWrappedImageView(device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void FSR::CreateRenderPasses(const Device& device) { + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + for (auto& images : m_dynamic_images) { + images.framebuffers[Easu] = CreateWrappedFramebuffer(device, m_renderpass, images.image_views[Easu], m_extent); + images.framebuffers[Rcas] = CreateWrappedFramebuffer(device, m_renderpass, images.image_views[Rcas], m_extent); } } @@ -76,8 +88,8 @@ void FSR::CreateDescriptorSetLayout(const Device& device) { void FSR::CreateDescriptorSets(const Device& device) { std::vector layouts(MaxFsrStage, *m_descriptor_set_layout); - for (size_t i = 0; i < m_image_count; ++i) - m_descriptor_sets.push_back(CreateWrappedDescriptorSets(m_descriptor_pool, layouts)); + for (auto& images : m_dynamic_images) + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); } void FSR::CreatePipelineLayouts(const Device& device) { @@ -100,39 +112,51 @@ void FSR::CreatePipelineLayouts(const Device& device) { } void FSR::CreatePipelines(const Device& device) { - m_easu_pipeline = CreateWrappedPipeline(device, VK_FORMAT_R16G16B16A16_SFLOAT, - m_pipeline_layout, + m_easu_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_easu_shader)); - m_rcas_pipeline = CreateWrappedPipeline(device, VK_FORMAT_R16G16B16A16_SFLOAT, - m_pipeline_layout, + m_rcas_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_rcas_shader)); } void FSR::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { - const vk::DescriptorSets& descriptor_sets = m_descriptor_sets[image_index]; + Images& images = m_dynamic_images[image_index]; std::vector image_infos; image_infos.reserve(2); std::vector updates{ - CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, descriptor_sets[Easu], 0), - CreateWriteDescriptorSet(image_infos, *m_sampler, *m_image_views[Easu], descriptor_sets[Rcas], 0) + CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, images.descriptor_sets[Easu], 0), + CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu], images.descriptor_sets[Rcas], 0) }; device.GetLogical().UpdateDescriptorSets(updates, {}); } +void FSR::UploadImages(const Device& device, Scheduler& scheduler) { + if (!m_images_ready) { + m_images_ready = true; + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.images[Easu]); + ClearColorImage(cmdbuf, *image.images[Rcas]); + } + }); + scheduler.Finish(); + } +} + VkImageView FSR::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect) { - const vk::DescriptorSets& descriptor_sets = m_descriptor_sets[image_index]; + Images& images = m_dynamic_images[image_index]; - VkImage easu_image = *m_images[Easu]; - VkImage rcas_image = *m_images[Rcas]; - VkDescriptorSet easu_descriptor_set = descriptor_sets[Easu]; - VkDescriptorSet rcas_descriptor_set = descriptor_sets[Rcas]; - VkImageView easu_view = *m_image_views[Easu]; - VkImageView rcas_view = *m_image_views[Rcas]; + VkImage easu_image = *images.images[Easu]; + VkImage rcas_image = *images.images[Rcas]; + VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu]; + VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas]; + VkFramebuffer easu_framebuffer = *images.framebuffers[Easu]; + VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas]; VkPipeline easu_pipeline = *m_easu_pipeline; VkPipeline rcas_pipeline = *m_rcas_pipeline; VkPipelineLayout pipeline_layout = *m_pipeline_layout; + VkRenderPass renderpass = *m_renderpass; VkExtent2D extent = m_extent; const f32 input_image_width = static_cast(input_image_extent.width); @@ -155,36 +179,35 @@ VkImageView FSR::Draw(const Device& device, Scheduler& scheduler, size_t image_i static_cast(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; FsrRcasCon(rcas_con.data(), sharpening); + UploadImages(device, scheduler); UpdateDescriptorSets(device, source_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, easu_view, extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, easu_descriptor_set, {}); cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, rcas_view, extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, rcas_descriptor_set, {}); cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); }); - return rcas_view; + return *images.image_views[Rcas]; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.h b/src/video_core/renderer_vulkan/present/fsr.h index 373a985339..7762480757 100644 --- a/src/video_core/renderer_vulkan/present/fsr.h +++ b/src/video_core/renderer_vulkan/present/fsr.h @@ -24,6 +24,7 @@ public: private: void CreateImages(const Device& device); + void CreateRenderPasses(const Device& device); void CreateSampler(const Device& device); void CreateShaders(const Device& device); void CreateDescriptorPool(const Device& device); @@ -32,6 +33,7 @@ private: void CreatePipelineLayouts(const Device& device); void CreatePipelines(const Device& device); + void UploadImages(const Device& device, Scheduler& scheduler); void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); MemoryAllocator& m_memory_allocator; @@ -52,11 +54,17 @@ private: vk::ShaderModule m_rcas_shader; vk::Pipeline m_easu_pipeline; vk::Pipeline m_rcas_pipeline; + vk::RenderPass m_renderpass; vk::Sampler m_sampler; - std::array m_images; - std::array m_image_views; - std::vector m_descriptor_sets; + struct Images { + vk::DescriptorSets descriptor_sets; + std::array images; + std::array image_views; + std::array framebuffers; + }; + std::vector m_dynamic_images; + bool m_images_ready{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fxaa.cpp b/src/video_core/renderer_vulkan/present/fxaa.cpp index 6eb07b004f..62f79d490c 100644 --- a/src/video_core/renderer_vulkan/present/fxaa.cpp +++ b/src/video_core/renderer_vulkan/present/fxaa.cpp @@ -21,6 +21,7 @@ FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, , m_image_count(u32(image_count)) { CreateImages(device, allocator); + CreateRenderPasses(device); CreateSampler(device); CreateShaders(device); CreateDescriptorPool(device); @@ -33,8 +34,20 @@ FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, FXAA::~FXAA() = default; void FXAA::CreateImages(const Device& device, MemoryAllocator& allocator) { - m_image = CreateWrappedImage(allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - m_image_view = CreateWrappedImageView(device, m_image, VK_FORMAT_R16G16B16A16_SFLOAT); + for (u32 i = 0; i < m_image_count; i++) { + Image& image = m_dynamic_images.emplace_back(); + image.image = CreateWrappedImage(allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + image.image_view = CreateWrappedImageView(device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void FXAA::CreateRenderPasses(const Device& device) { + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& image : m_dynamic_images) { + image.framebuffer = + CreateWrappedFramebuffer(device, m_renderpass, image.image_view, m_extent); + } } void FXAA::CreateSampler(const Device& device) { @@ -60,8 +73,8 @@ void FXAA::CreateDescriptorSetLayouts(const Device& device) { void FXAA::CreateDescriptorSets(const Device& device) { VkDescriptorSetLayout layout = *m_descriptor_set_layout; - for (u32 i = 0; i < m_image_count; i++) { - m_descriptor_sets.push_back(CreateWrappedDescriptorSets(m_descriptor_pool, {layout})); + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout}); } } @@ -70,48 +83,65 @@ void FXAA::CreatePipelineLayouts(const Device& device) { } void FXAA::CreatePipelines(const Device& device) { - m_pipeline = CreateWrappedPipeline(device, VK_FORMAT_R16G16B16A16_SFLOAT, m_pipeline_layout, + m_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vertex_shader, m_fragment_shader)); } void FXAA::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { - const VkDescriptorSet descriptor_set = m_descriptor_sets[image_index][0]; + Image& image = m_dynamic_images[image_index]; std::vector image_infos; std::vector updates; image_infos.reserve(2); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, descriptor_set, 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, descriptor_set, 1)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); device.GetLogical().UpdateDescriptorSets(updates, {}); } +void FXAA::UploadImages(const Device& device, Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.image); + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + void FXAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) { + const Image& image{m_dynamic_images[image_index]}; const VkImage input_image{*inout_image}; - const VkImage output_image{*m_image}; - const VkImageView output_view{*m_image_view}; - const VkDescriptorSet descriptor_set{m_descriptor_sets[image_index][0]}; + const VkImage output_image{*image.image}; + const VkDescriptorSet descriptor_set{image.descriptor_sets[0]}; + const VkFramebuffer framebuffer{*image.framebuffer}; + const VkRenderPass renderpass{*m_renderpass}; const VkPipeline pipeline{*m_pipeline}; const VkPipelineLayout layout{*m_pipeline_layout}; const VkExtent2D extent{m_extent}; + UploadImages(device, scheduler); UpdateDescriptorSets(device, *inout_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, output_view, extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); }); - *inout_image = output_image; - *inout_image_view = output_view; + *inout_image = *image.image; + *inout_image_view = *image.image_view; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fxaa.h b/src/video_core/renderer_vulkan/present/fxaa.h index 7f5599db16..7eaf31f81c 100644 --- a/src/video_core/renderer_vulkan/present/fxaa.h +++ b/src/video_core/renderer_vulkan/present/fxaa.h @@ -25,6 +25,7 @@ public: private: void CreateImages(const Device& device, MemoryAllocator& allocator); + void CreateRenderPasses(const Device& device); void CreateSampler(const Device& device); void CreateShaders(const Device& device); void CreateDescriptorPool(const Device& device); @@ -33,10 +34,15 @@ private: void CreatePipelineLayouts(const Device& device); void CreatePipelines(const Device& device); void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); + void UploadImages(const Device& device, Scheduler& scheduler); - vk::Image m_image{}; - vk::ImageView m_image_view{}; - std::vector m_descriptor_sets{}; + struct Image { + vk::DescriptorSets descriptor_sets{}; + vk::Framebuffer framebuffer{}; + vk::Image image{}; + vk::ImageView image_view{}; + }; + std::vector m_dynamic_images{}; const VkExtent2D m_extent; const u32 m_image_count; vk::ShaderModule m_vertex_shader{}; @@ -45,7 +51,9 @@ private: vk::DescriptorSetLayout m_descriptor_set_layout{}; vk::PipelineLayout m_pipeline_layout{}; vk::Pipeline m_pipeline{}; + vk::RenderPass m_renderpass{}; vk::Sampler m_sampler{}; + bool m_images_ready{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index f7152a31eb..9f4aaf390f 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -60,34 +60,25 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { } // Anonymous namespace -Layer::Layer(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, - Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, - size_t generation_count_, VkExtent2D output_size, VkDescriptorSetLayout layout, - const PresentFilters& filters_) +Layer::Layer(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_) : memory_allocator(memory_allocator_) , scheduler(scheduler_) , device_memory(device_memory_) , filters(filters_) , image_count(image_count_) - , generation_count(generation_count_) - , generation_ticks(generation_count_) { CreateDescriptorPool(device); CreateDescriptorSets(device, layout); - const size_t slot_count = image_count + generation_count; if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { - sr_filter.emplace(device, memory_allocator, slot_count, output_size); + sr_filter.emplace(device, memory_allocator, image_count, output_size); } else if (filters.get_scaling_filter() == Settings::ScalingFilter::Sgsr) { - sr_filter.emplace(device, memory_allocator, slot_count, output_size, false); + sr_filter.emplace(device, memory_allocator, image_count, output_size, false); } else if (filters.get_scaling_filter() == Settings::ScalingFilter::SgsrEdge) { - sr_filter.emplace(device, memory_allocator, slot_count, output_size, true); + sr_filter.emplace(device, memory_allocator, image_count, output_size, true); } } Layer::~Layer() { - for (const u64 tick : generation_ticks) { - scheduler.Wait(tick); - } ReleaseRawImages(); } @@ -103,11 +94,14 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; const bool use_accelerated = texture_info.has_value(); + const bool is_applet = + (framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit( + Service::Nvnflinger::LayerStackId::Recording)) == 0; RefreshResources(device, framebuffer); SetAntiAliasPass(device); #ifdef HAS_RESHADE - SetPostProcessPass(device); + SetPostProcessPass(device, is_applet); #endif // Finish any pending renderpass @@ -124,43 +118,34 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; VkImageView source_image_view = texture_info ? texture_info->image_view : *raw_image_views[image_index]; - const VkExtent2D render_extent{ - .width = scaled_width, - .height = scaled_height, - }; - const VkExtent2D processed_extent{ - .width = Settings::values.resolution_info.ScaleUp(raw_width), - .height = Settings::values.resolution_info.ScaleUp(raw_height), - }; - VkExtent2D source_extent = render_extent; if (auto* fxaa = std::get_if(&anti_alias)) { fxaa->Draw(device, scheduler, image_index, &source_image, &source_image_view); - source_extent = processed_extent; } else if (auto* smaa = std::get_if(&anti_alias)) { smaa->Draw(device, scheduler, image_index, &source_image, &source_image_view); - source_extent = processed_extent; } #ifdef HAS_RESHADE if (post_process.has_value()) { post_process->Draw(device, scheduler, image_index, &source_image, &source_image_view); - source_extent = processed_extent; } #endif auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); - generation_source = source_image_view; - generation_extent = source_extent; - generation_render_extent = render_extent; - generation_crop = crop_rect; + const VkExtent2D render_extent{ + .width = scaled_width, + .height = scaled_height, + }; if (auto* fsr = std::get_if(&sr_filter)) { source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); crop_rect = {0, 0, 1, 1}; } else if (auto* sgsr = std::get_if(&sr_filter)) { - source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); - crop_rect = {0, 0, 1, 1}; + if (!is_applet) { + source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, + source_image_view, render_extent, crop_rect); + crop_rect = {0, 0, 1, 1}; + } } SetMatrixData(device, *out_push_constants, layout); @@ -170,43 +155,12 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c *out_descriptor_set = descriptor_sets[image_index]; } -void Layer::ConfigureGenerated(const Device& device, PresentPushConstants* out_push_constants, - VkDescriptorSet* out_descriptor_set, VkSampler sampler, - size_t generation, VkImage image, VkImageView view, - const Layout::FramebufferLayout& layout) { - const size_t slot = image_count + generation; - generation_ticks[generation] = scheduler.CurrentTick(); - - VkImageView source_image_view = view; - Common::Rectangle crop_rect = generation_crop; - if (auto* fsr = std::get_if(&sr_filter)) { - source_image_view = - fsr->Draw(device, scheduler, slot, image, view, generation_render_extent, crop_rect); - crop_rect = {0, 0, 1, 1}; - } else if (auto* sgsr = std::get_if(&sr_filter)) { - source_image_view = - sgsr->Draw(device, scheduler, slot, image, view, generation_render_extent, crop_rect); - crop_rect = {0, 0, 1, 1}; - } - - SetMatrixData(device, *out_push_constants, layout); - SetVertexData(device, *out_push_constants, layout, crop_rect); - - UpdateDescriptorSet(device, source_image_view, sampler, slot); - *out_descriptor_set = descriptor_sets[slot]; -} - -bool Layer::IsGenerationFree(size_t generation) const { - return generation < generation_count && scheduler.IsFree(generation_ticks[generation]); -} - void Layer::CreateDescriptorPool(const Device& device) { - const size_t slot_count = image_count + generation_count; - descriptor_pool = CreateWrappedDescriptorPool(device, slot_count, slot_count); + descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); } void Layer::CreateDescriptorSets(const Device& device, VkDescriptorSetLayout layout) { - const std::vector layouts(image_count + generation_count, layout); + const std::vector layouts(image_count, layout); descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); } @@ -280,7 +234,11 @@ void Layer::SetAntiAliasPass(const Device& device) { } #ifdef HAS_RESHADE -void Layer::SetPostProcessPass(const Device& device) { +void Layer::SetPostProcessPass(const Device& device, bool is_applet) { + if (is_applet) { + post_process.reset(); + return; + } const VkExtent2D render_area{ .width = Settings::values.resolution_info.ScaleUp(raw_width), .height = Settings::values.resolution_info.ScaleUp(raw_height), @@ -395,9 +353,7 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i buffer.Flush(); // Ensure host writes are visible before the GPU copy. } - const VkBufferImageCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .pNext = nullptr, + const VkBufferImageCopy copy{ .bufferOffset = image_offset, .bufferRowLength = 0, .bufferImageHeight = 0, @@ -418,13 +374,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i }; scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { const VkImage image = *raw_images[index]; - const VkImageMemoryBarrier2 base_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier base_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_NONE, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = VK_PIPELINE_STAGE_2_NONE, - .dstAccessMask = VK_ACCESS_2_NONE, + .srcAccessMask = 0, + .dstAccessMask = 0, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -438,24 +392,23 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i .layerCount = 1, }, }; - VkImageMemoryBarrier2 read_barrier = base_barrier; - read_barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT; - read_barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - read_barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + VkImageMemoryBarrier read_barrier = base_barrier; + read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - VkImageMemoryBarrier2 write_barrier = base_barrier; - write_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - write_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; - write_barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; - write_barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; + VkImageMemoryBarrier write_barrier = base_barrier; + write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, write_barrier); }); } diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index 422eca6d2f..b965ef7c49 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -51,7 +51,7 @@ class Layer final { public: explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, - size_t generation_count, VkExtent2D output_size, VkDescriptorSetLayout layout, + VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters); ~Layer(); @@ -61,21 +61,6 @@ public: const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout& layout); - void ConfigureGenerated(const Device& device, PresentPushConstants* out_push_constants, - VkDescriptorSet* out_descriptor_set, VkSampler sampler, - size_t generation, VkImage image, VkImageView view, - const Layout::FramebufferLayout& layout); - - [[nodiscard]] bool IsGenerationFree(size_t generation) const; - - [[nodiscard]] VkImageView GenerationSource() const { - return generation_source; - } - - [[nodiscard]] VkExtent2D GenerationExtent() const { - return generation_extent; - } - private: void CreateDescriptorPool(const Device& device); void CreateDescriptorSets(const Device& device, VkDescriptorSetLayout layout); @@ -85,7 +70,7 @@ private: void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer); void SetAntiAliasPass(const Device& device); #ifdef HAS_RESHADE - void SetPostProcessPass(const Device& device); + void SetPostProcessPass(const Device& device, bool is_applet); #endif void ReleaseRawImages(); @@ -103,7 +88,6 @@ private: Tegra::MaxwellDeviceMemoryManager& device_memory; const PresentFilters& filters; const size_t image_count{}; - const size_t generation_count{}; vk::DescriptorPool descriptor_pool{}; vk::DescriptorSets descriptor_sets{}; @@ -124,11 +108,6 @@ private: VkExtent2D post_process_extent{}; #endif std::vector resource_ticks{}; - std::vector generation_ticks{}; - VkImageView generation_source{}; - VkExtent2D generation_extent{}; - VkExtent2D generation_render_extent{}; - Common::Rectangle generation_crop{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_chain.cpp b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp index a7ecfc5d71..972b13a1c4 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_chain.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp @@ -15,37 +15,9 @@ namespace Vulkan { namespace { constexpr u32 FIXED_DESCRIPTOR_SETS = 64; -constexpr u32 DESCRIPTOR_SETS_PER_SLOT = 100; +constexpr u32 DESCRIPTOR_SETS_PER_SLOT = 112; constexpr size_t FIRST_DELTA_LEVEL = 4; -[[nodiscard]] constexpr size_t DeltaStartWave(size_t index) { - if (index == 0) { - return 0; - } - return (FIRST_DELTA_LEVEL + index) * LSFG_GAMMA_STAGES; -} - -constexpr size_t GENERATION_WAVES = - DeltaStartWave(LSFG_DELTA_INSTANCES - 1) + LSFG_DELTA_STAGES; - -template -void ForEachWaveStage(std::array& gamma, - std::array& delta, size_t wave, - Func&& func) { - for (size_t i = 0; i < gamma.size(); ++i) { - const size_t start = i * LSFG_GAMMA_STAGES; - if (wave >= start && wave - start < LSFG_GAMMA_STAGES) { - func(gamma[i], wave - start); - } - } - for (size_t i = 0; i < delta.size(); ++i) { - const size_t start = DeltaStartWave(i); - if (wave >= start && wave - start < LSFG_DELTA_STAGES) { - func(delta[i], wave - start); - } - } -} - } // Anonymous namespace LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator, @@ -75,7 +47,7 @@ LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator, const size_t level = LSFG_MIP_LEVELS - 1 - i; gamma[i] = LsfgGamma(device, memory_allocator, shaders, resources, descriptor_pool, alpha[level].Outputs(), - beta.Output((std::min)(level, LSFG_BETA_OUTPUTS - 1)), + beta.Output(std::min(level, LSFG_BETA_OUTPUTS - 1)), i == 0 ? nullptr : &gamma[i - 1].Output()); if (i < FIRST_DELTA_LEVEL) { @@ -116,20 +88,16 @@ void LsfgChain::DispatchShared(vk::CommandBuffer cmdbuf, u64 frame_count) { } void LsfgChain::DispatchGeneration(vk::CommandBuffer cmdbuf, u64 frame_count, - size_t generation_count, size_t generation, VkImage image, - VkExtent2D extent) { + size_t generation_count, size_t generation, u32 target, + VkImage image, VkExtent2D extent) { const size_t slot = LsfgGenerationSlot(generation_count, generation); - for (size_t wave = 0; wave < GENERATION_WAVES; ++wave) { - LsfgBarriers barriers(cmdbuf); - ForEachWaveStage(gamma, delta, wave, [&barriers, frame_count](auto& pass, size_t stage) { - pass.PushBarriers(barriers, frame_count, stage); - }); - barriers.Build(); - ForEachWaveStage(gamma, delta, wave, [cmdbuf, frame_count, slot](auto& pass, size_t stage) { - pass.DispatchStage(cmdbuf, frame_count, slot, stage); - }); + for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { + gamma[i].Dispatch(cmdbuf, frame_count, slot); + if (i >= FIRST_DELTA_LEVEL) { + delta[i - FIRST_DELTA_LEVEL].Dispatch(cmdbuf, frame_count, slot); + } } - generate.Dispatch(cmdbuf, frame_count, slot, image, extent); + generate.Dispatch(cmdbuf, frame_count, slot, target, image, extent); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_chain.h b/src/video_core/renderer_vulkan/present/lsfg_chain.h index 4561d3a092..c8879f74a9 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_chain.h +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.h @@ -35,11 +35,11 @@ public: void DispatchShared(vk::CommandBuffer cmdbuf, u64 frame_count); void DispatchGeneration(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation_count, - size_t generation, VkImage image, VkExtent2D extent); + size_t generation, u32 target, VkImage image, VkExtent2D extent); - void SetTarget(const Device& device, size_t generation_count, size_t generation, + void SetTarget(const Device& device, size_t generation_count, size_t generation, u32 target, VkImageView view) { - generate.SetTarget(device, LsfgGenerationSlot(generation_count, generation), view); + generate.SetTarget(device, LsfgGenerationSlot(generation_count, generation), target, view); } [[nodiscard]] LsfgImage& Input(u64 frame_count) { diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.cpp b/src/video_core/renderer_vulkan/present/lsfg_common.cpp index 0e34f782e7..d2df2755e5 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_common.cpp @@ -44,8 +44,7 @@ vk::Image CreateChainImage(MemoryAllocator& memory_allocator, VkExtent2D extent, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -68,14 +67,12 @@ vk::Buffer CreateUniformBuffer(MemoryAllocator& memory_allocator, VkDeviceSize s return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::Upload); } -VkImageMemoryBarrier2 MakeBarrier(const LsfgImage& image, VkAccessFlags2 src_access, - VkAccessFlags2 dst_access) { - return VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, +VkImageMemoryBarrier MakeBarrier(const LsfgImage& image, VkAccessFlags src_access, + VkAccessFlags dst_access) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, .srcAccessMask = src_access, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, .dstAccessMask = dst_access, .oldLayout = image.Layout(), .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -96,24 +93,24 @@ VkImageMemoryBarrier2 MakeBarrier(const LsfgImage& image, VkAccessFlags2 src_acc LsfgImage::LsfgImage(const Device& device, MemoryAllocator& memory_allocator, VkExtent2D extent_, VkFormat format_) - : extent{(std::max)(1u, extent_.width), (std::max)(1u, extent_.height)}, format{format_} { + : extent{std::max(1u, extent_.width), std::max(1u, extent_.height)}, format{format_} { image = CreateChainImage(memory_allocator, extent, format); view = CreateWrappedImageView(device, image, format); } -LsfgBarriers& LsfgBarriers::Push(LsfgImage& image, VkAccessFlags2 src_access, - VkAccessFlags2 dst_access) { +LsfgBarriers& LsfgBarriers::Push(LsfgImage& image, VkAccessFlags src_access, + VkAccessFlags dst_access) { barriers.push_back(MakeBarrier(image, src_access, dst_access)); image.SetLayout(VK_IMAGE_LAYOUT_GENERAL); return *this; } LsfgBarriers& LsfgBarriers::WriteToRead(LsfgImage& image) { - return Push(image, VK_ACCESS_2_SHADER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT); + return Push(image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage& image) { - return Push(image, VK_ACCESS_2_SHADER_READ_BIT, VK_ACCESS_2_SHADER_WRITE_BIT); + return Push(image, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT); } LsfgBarriers& LsfgBarriers::WriteToRead(LsfgImage* image) { @@ -125,13 +122,11 @@ LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage* image) { } LsfgBarriers& LsfgBarriers::DiscardToWrite(VkImage image) { - barriers.push_back(VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + barriers.push_back(VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -204,7 +199,8 @@ void LsfgBarriers::Build() { if (barriers.empty()) { return; } - cmdbuf.PipelineBarrier(0, {}, {}, barriers); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers); barriers.clear(); } @@ -287,6 +283,7 @@ LsfgPass::LsfgPass(const Device& device, const LsfgShaders& shaders, u32 shader_ for (const auto& [count, type] : bindings) { types.insert(types.end(), count, type); } + descriptor_count = static_cast(types.size()); descriptor_set_layout = CreateWrappedDescriptorSetLayout( device, std::span{types}, VK_SHADER_STAGE_COMPUTE_BIT); diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.h b/src/video_core/renderer_vulkan/present/lsfg_common.h index 68e4d16f57..d1e3271a6d 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.h +++ b/src/video_core/renderer_vulkan/present/lsfg_common.h @@ -27,6 +27,7 @@ constexpr VkFormat LSFG_FLOW_FORMAT = VK_FORMAT_R8_UNORM; constexpr VkFormat LSFG_MOTION_FORMAT = VK_FORMAT_R16G16B16A16_SFLOAT; constexpr size_t LSFG_HISTORY_SLOTS = 3; +constexpr size_t LSFG_MAX_TARGETS = 7; constexpr size_t LSFG_MAX_GENERATIONS = 3; constexpr size_t LSFG_GENERATION_SLOTS = LSFG_MAX_GENERATIONS * (LSFG_MAX_GENERATIONS + 1) / 2; @@ -146,10 +147,10 @@ public: void Build(); private: - LsfgBarriers& Push(LsfgImage& image, VkAccessFlags2 src_access, VkAccessFlags2 dst_access); + LsfgBarriers& Push(LsfgImage& image, VkAccessFlags src_access, VkAccessFlags dst_access); vk::CommandBuffer cmdbuf; - std::vector barriers; + std::vector barriers; }; class LsfgDescriptorWriter { @@ -203,6 +204,10 @@ public: return *descriptor_set_layout; } + [[nodiscard]] u32 DescriptorCount() const { + return descriptor_count; + } + void Bind(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const; void BindPipeline(vk::CommandBuffer cmdbuf) const; void BindSet(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const; @@ -211,6 +216,7 @@ private: vk::DescriptorSetLayout descriptor_set_layout; vk::PipelineLayout pipeline_layout; vk::Pipeline pipeline; + u32 descriptor_count{}; }; [[nodiscard]] vk::DescriptorPool CreateLsfgDescriptorPool(const Device& device, u32 max_sets); diff --git a/src/video_core/renderer_vulkan/present/lsfg_delta.cpp b/src/video_core/renderer_vulkan/present/lsfg_delta.cpp index 9ced1f5a47..dc6eec383f 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_delta.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_delta.cpp @@ -196,68 +196,88 @@ LsfgDelta::LsfgDelta(const Device& device, MemoryAllocator& memory_allocator, } } -void LsfgDelta::PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage) { - auto& history = (*inputs)[frame_count % LSFG_HISTORY_SLOTS]; - auto& previous_history = (*inputs)[(frame_count + 2) % LSFG_HISTORY_SLOTS]; - switch (stage) { - case 0: - barriers.WriteToReadAll(previous_history) - .WriteToReadAll(history) - .WriteToRead(previous_gamma) - .ReadToWriteAll(temp1); - break; - case 1: - case 3: - barriers.WriteToReadAll(temp1).ReadToWriteAll(temp2); - break; - case 2: - barriers.WriteToReadAll(temp2).ReadToWriteAll(temp1); - break; - case 4: - barriers.WriteToReadAll(temp2) - .WriteToRead(previous_gamma) - .WriteToRead(*flow_input) - .ReadToWrite(out_image1); - break; - case 5: - barriers.WriteToReadAll(previous_history) - .WriteToReadAll(history) - .WriteToRead(previous_gamma) - .WriteToRead(previous1) - .ReadToWriteAll(temp2); - break; - case 6: - case 8: - barriers.WriteToReadAll(temp2).ReadToWrite(temp1[0]).ReadToWrite(temp1[1]); - break; - case 7: - barriers.WriteToRead(temp1[0]).WriteToRead(temp1[1]).ReadToWriteAll(temp2); - break; - default: - barriers.WriteToRead(temp1[0]) - .WriteToRead(temp1[1]) - .WriteToRead(previous2) - .ReadToWrite(out_image2); - break; - } -} - -void LsfgDelta::DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, - size_t stage) const { +void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot) { const Generation& pass = generations[slot]; - const size_t history = frame_count % LSFG_HISTORY_SLOTS; - VkDescriptorSet set = pass.first_descriptor_sets[history]; - if (stage == 5) { - set = pass.sixth_descriptor_sets[history]; - } else if (stage > 5) { - set = pass.descriptor_sets[stage - 2]; - } else if (stage != 0) { - set = pass.descriptor_sets[stage - 1]; - } - passes[stage].Bind(cmdbuf, set); const VkExtent2D extent = temp1[0].Extent(); - cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1); + const u32 groups_x = GroupCount(extent.width); + const u32 groups_y = GroupCount(extent.height); + + const size_t history = frame_count % LSFG_HISTORY_SLOTS; + const size_t previous_history = (frame_count + 2) % LSFG_HISTORY_SLOTS; + + LsfgBarriers(cmdbuf) + .WriteToReadAll((*inputs)[previous_history]) + .WriteToReadAll((*inputs)[history]) + .WriteToRead(previous_gamma) + .ReadToWriteAll(temp1) + .Build(); + passes[0].Bind(cmdbuf, pass.first_descriptor_sets[history]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); + passes[1].Bind(cmdbuf, pass.descriptor_sets[0]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf).WriteToReadAll(temp2).ReadToWriteAll(temp1).Build(); + passes[2].Bind(cmdbuf, pass.descriptor_sets[1]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); + passes[3].Bind(cmdbuf, pass.descriptor_sets[2]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll(temp2) + .WriteToRead(previous_gamma) + .WriteToRead(*flow_input) + .ReadToWrite(out_image1) + .Build(); + passes[4].Bind(cmdbuf, pass.descriptor_sets[3]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll((*inputs)[previous_history]) + .WriteToReadAll((*inputs)[history]) + .WriteToRead(previous_gamma) + .WriteToRead(previous1) + .ReadToWriteAll(temp2) + .Build(); + passes[5].Bind(cmdbuf, pass.sixth_descriptor_sets[history]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll(temp2) + .ReadToWrite(temp1[0]) + .ReadToWrite(temp1[1]) + .Build(); + passes[6].Bind(cmdbuf, pass.descriptor_sets[4]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToRead(temp1[0]) + .WriteToRead(temp1[1]) + .ReadToWriteAll(temp2) + .Build(); + passes[7].Bind(cmdbuf, pass.descriptor_sets[5]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll(temp2) + .ReadToWrite(temp1[0]) + .ReadToWrite(temp1[1]) + .Build(); + passes[8].Bind(cmdbuf, pass.descriptor_sets[6]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToRead(temp1[0]) + .WriteToRead(temp1[1]) + .WriteToRead(previous2) + .ReadToWrite(out_image2) + .Build(); + passes[9].Bind(cmdbuf, pass.descriptor_sets[7]); + cmdbuf.Dispatch(groups_x, groups_y, 1); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_delta.h b/src/video_core/renderer_vulkan/present/lsfg_delta.h index a3beda0b77..1bbcfe6745 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_delta.h +++ b/src/video_core/renderer_vulkan/present/lsfg_delta.h @@ -27,9 +27,7 @@ public: LsfgImageHistory& inputs, LsfgImage& flow_input, LsfgImage* previous_gamma, LsfgImage* previous1, LsfgImage* previous2); - void PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage); - - void DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, size_t stage) const; + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot); [[nodiscard]] LsfgImage& Output1() { return out_image1; diff --git a/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp b/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp index fff0b57d0e..067d124b6f 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp @@ -133,43 +133,53 @@ LsfgGamma::LsfgGamma(const Device& device, MemoryAllocator& memory_allocator, } } -void LsfgGamma::PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage) { - switch (stage) { - case 0: - barriers.WriteToReadAll((*inputs)[(frame_count + 2) % LSFG_HISTORY_SLOTS]) - .WriteToReadAll((*inputs)[frame_count % LSFG_HISTORY_SLOTS]) - .WriteToRead(previous) - .ReadToWriteAll(temp1); - break; - case 1: - barriers.WriteToReadAll(temp1).ReadToWriteAll(temp2); - break; - case 2: - barriers.WriteToReadAll(temp2).ReadToWrite(temp1[0]).ReadToWrite(temp1[1]); - break; - case 3: - barriers.WriteToRead(temp1[0]).WriteToRead(temp1[1]).ReadToWriteAll(temp2); - break; - default: - barriers.WriteToReadAll(temp2) - .WriteToRead(previous) - .WriteToRead(*flow_input) - .ReadToWrite(out_image); - break; - } -} - -void LsfgGamma::DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, - size_t stage) const { +void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot) { const Generation& pass = generations[slot]; - VkDescriptorSet set = pass.first_descriptor_sets[frame_count % LSFG_HISTORY_SLOTS]; - if (stage != 0) { - set = pass.descriptor_sets[stage - 1]; - } - passes[stage].Bind(cmdbuf, set); const VkExtent2D extent = temp1[0].Extent(); - cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1); + const u32 groups_x = GroupCount(extent.width); + const u32 groups_y = GroupCount(extent.height); + + const size_t history = frame_count % LSFG_HISTORY_SLOTS; + const size_t previous_history = (frame_count + 2) % LSFG_HISTORY_SLOTS; + + LsfgBarriers(cmdbuf) + .WriteToReadAll((*inputs)[previous_history]) + .WriteToReadAll((*inputs)[history]) + .WriteToRead(previous) + .ReadToWriteAll(temp1) + .Build(); + passes[0].Bind(cmdbuf, pass.first_descriptor_sets[history]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); + passes[1].Bind(cmdbuf, pass.descriptor_sets[0]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll(temp2) + .ReadToWrite(temp1[0]) + .ReadToWrite(temp1[1]) + .Build(); + passes[2].Bind(cmdbuf, pass.descriptor_sets[1]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToRead(temp1[0]) + .WriteToRead(temp1[1]) + .ReadToWriteAll(temp2) + .Build(); + passes[3].Bind(cmdbuf, pass.descriptor_sets[2]); + cmdbuf.Dispatch(groups_x, groups_y, 1); + + LsfgBarriers(cmdbuf) + .WriteToReadAll(temp2) + .WriteToRead(previous) + .WriteToRead(*flow_input) + .ReadToWrite(out_image) + .Build(); + passes[4].Bind(cmdbuf, pass.descriptor_sets[3]); + cmdbuf.Dispatch(groups_x, groups_y, 1); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_gamma.h b/src/video_core/renderer_vulkan/present/lsfg_gamma.h index cda230d449..848c380bfe 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_gamma.h +++ b/src/video_core/renderer_vulkan/present/lsfg_gamma.h @@ -26,9 +26,7 @@ public: LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImageHistory& inputs, LsfgImage& flow_input, LsfgImage* previous); - void PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage); - - void DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, size_t stage) const; + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot); [[nodiscard]] LsfgImage& Output() { return out_image; diff --git a/src/video_core/renderer_vulkan/present/lsfg_generate.cpp b/src/video_core/renderer_vulkan/present/lsfg_generate.cpp index f26c27c92c..4473b59a2f 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_generate.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_generate.cpp @@ -22,15 +22,12 @@ constexpr u32 DISPATCH_TILE_SHIFT = 4; return (size + (1u << DISPATCH_TILE_SHIFT) - 1) >> DISPATCH_TILE_SHIFT; } -VkImageMemoryBarrier2 MakeTargetBarrier(VkImage image, VkPipelineStageFlags2 src_stage, - VkAccessFlags2 src_access, VkPipelineStageFlags2 dst_stage, - VkAccessFlags2 dst_access, VkImageLayout old_layout) { - return VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, +VkImageMemoryBarrier MakeTargetBarrier(VkImage image, VkAccessFlags src_access, + VkAccessFlags dst_access, VkImageLayout old_layout) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = src_stage, .srcAccessMask = src_access, - .dstStageMask = dst_stage, .dstAccessMask = dst_access, .oldLayout = old_layout, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -66,21 +63,25 @@ LsfgGenerate::LsfgGenerate(const Device& device, const LsfgShaders& shaders, edge_sampler = resources.GetSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false); - const std::vector layouts(LSFG_GENERATION_SLOTS * 2, pass.SetLayout()); + const std::vector layouts( + LSFG_GENERATION_SLOTS * LSFG_MAX_TARGETS * 2, pass.SetLayout()); owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); size_t next = 0; for (size_t slot = 0; slot < LSFG_GENERATION_SLOTS; ++slot) { - Generation& entry = generations[slot]; - entry.buffer = resources.GetBuffer(LsfgSlotTimestamp(slot)); - for (auto& set : entry.descriptor_sets) { - set = owned_sets[next++]; + Generation& target = generations[slot]; + target.buffer = resources.GetBuffer(LsfgSlotTimestamp(slot)); + + for (auto& entry : target.targets) { + for (auto& set : entry.descriptor_sets) { + set = owned_sets[next++]; + } } } } -void LsfgGenerate::SetTarget(const Device& device, size_t slot, VkImageView view) { - Generation& entry = generations[slot]; +void LsfgGenerate::SetTarget(const Device& device, size_t slot, u32 target, VkImageView view) { + Target& entry = generations[slot].targets[target]; if (entry.view == view) { return; } @@ -88,7 +89,7 @@ void LsfgGenerate::SetTarget(const Device& device, size_t slot, VkImageView view for (size_t i = 0; i < entry.descriptor_sets.size(); ++i) { LsfgDescriptorWriter(entry.descriptor_sets[i]) - .AddUniformBuffer(entry.buffer, LsfgResources::BufferSize()) + .AddUniformBuffer(generations[slot].buffer, LsfgResources::BufferSize()) .AddSampler(sampler) .AddSampler(edge_sampler) .AddSampledImage((*frames)[1 - i]) @@ -101,9 +102,9 @@ void LsfgGenerate::SetTarget(const Device& device, size_t slot, VkImageView view } } -void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, +void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, u32 target, VkImage image, VkExtent2D extent) { - const Generation& entry = generations[slot]; + const Target& entry = generations[slot].targets[target]; LsfgBarriers(cmdbuf) .WriteToReadAll(*frames) @@ -116,11 +117,14 @@ void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t sl pass.Bind(cmdbuf, entry.descriptor_sets[frame_count % entry.descriptor_sets.size()]); cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1); - const std::array after{MakeTargetBarrier(image, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - VK_ACCESS_2_SHADER_WRITE_BIT, - vk::PIPELINE_STAGE_IMAGE_USERS, - vk::ACCESS_IMAGE_USERS, VK_IMAGE_LAYOUT_GENERAL)}; - cmdbuf.PipelineBarrier(0, {}, {}, after); + const std::array after{MakeTargetBarrier( + image, VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL)}; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, {}, {}, after); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_generate.h b/src/video_core/renderer_vulkan/present/lsfg_generate.h index fedad97bdd..75cbd8d2e4 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_generate.h +++ b/src/video_core/renderer_vulkan/present/lsfg_generate.h @@ -23,15 +23,19 @@ public: vk::DescriptorPool& descriptor_pool, LsfgImagePair& frames, LsfgImage& motion, LsfgImage& detail1, LsfgImage& detail2); - void SetTarget(const Device& device, size_t slot, VkImageView view); + void SetTarget(const Device& device, size_t slot, u32 target, VkImageView view); - void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, VkImage image, - VkExtent2D extent); + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t slot, u32 target, + VkImage image, VkExtent2D extent); private: - struct Generation { + struct Target { std::array descriptor_sets{}; VkImageView view{}; + }; + + struct Generation { + std::array targets{}; VkBuffer buffer{}; }; diff --git a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp index c5cd1c7c86..3f31f58d68 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp @@ -40,10 +40,8 @@ LsfgMipmaps::LsfgMipmaps(const Device& device, MemoryAllocator& memory_allocator const VkExtent2D input_extent = (*frames)[0].Extent(); flow_extent = VkExtent2D{ - .width = (std::max)(1u, - static_cast(static_cast(input_extent.width) * flow_scale)), - .height = (std::max)(1u, - static_cast(static_cast(input_extent.height) * flow_scale)), + .width = std::max(1u, static_cast(static_cast(input_extent.width) * flow_scale)), + .height = std::max(1u, static_cast(static_cast(input_extent.height) * flow_scale)), }; for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { diff --git a/src/video_core/renderer_vulkan/present/lsfg_shaders.cpp b/src/video_core/renderer_vulkan/present/lsfg_shaders.cpp index 9aebbc3cd9..51c7e4c106 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_shaders.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_shaders.cpp @@ -10,7 +10,7 @@ namespace Vulkan { LsfgShaders::LsfgShaders(const Device& device) { if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor() || - !device.IsNativeFloat16Enabled()) { + !device.IsFloat16Supported()) { return; } diff --git a/src/video_core/renderer_vulkan/present/post_process.cpp b/src/video_core/renderer_vulkan/present/post_process.cpp index 7b5b81d7b5..07d75b7025 100644 --- a/src/video_core/renderer_vulkan/present/post_process.cpp +++ b/src/video_core/renderer_vulkan/present/post_process.cpp @@ -126,7 +126,58 @@ VkPrimitiveTopology ToTopology(reshadefx::primitive_topology topology) { } } -vk::Pipeline CreateFxPipeline(const Device& device, VkFormat format, +vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool clear) { + VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_LOAD; + VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL; + if (clear) { + load_op = VK_ATTACHMENT_LOAD_OP_CLEAR; + initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + const VkAttachmentDescription attachment{ + .flags = 0, + .format = format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = load_op, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = initial_layout, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkAttachmentReference reference{ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkSubpassDescription subpass{ + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = 1, + .pColorAttachments = &reference, + .pResolveAttachments = nullptr, + .pDepthStencilAttachment = nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + + return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .attachmentCount = 1, + .pAttachments = &attachment, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 0, + .pDependencies = nullptr, + }); +} + +vk::Pipeline CreateFxPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, VkShaderModule vertex_shader, VkShaderModule fragment_shader, const reshadefx::pass& pass) { @@ -247,10 +298,9 @@ vk::Pipeline CreateFxPipeline(const Device& device, VkFormat format, .pDynamicStates = dynamic_states.data(), }; - const VkPipelineRenderingCreateInfo rendering_ci = ColorRenderingInfo(format); return device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(stages.size()), .pStages = stages.data(), @@ -264,7 +314,7 @@ vk::Pipeline CreateFxPipeline(const Device& device, VkFormat format, .pColorBlendState = &color_blend, .pDynamicState = &dynamic_state, .layout = *layout, - .renderPass = VK_NULL_HANDLE, + .renderPass = *renderpass, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = 0, @@ -480,9 +530,7 @@ bool PostProcessChain::BuildEffects(const Device& device, MemoryAllocator& alloc for (const auto& pass : technique->passes) { Pass out; out.num_vertices = pass.num_vertices; - if (pass.clear_render_targets != 0) { - out.load_op = VK_ATTACHMENT_LOAD_OP_CLEAR; - } + out.clear = pass.clear_render_targets != 0; out.target_texture = NO_TEXTURE; out.extent = m_extent; @@ -548,9 +596,25 @@ bool PostProcessChain::BuildEffects(const Device& device, MemoryAllocator& alloc continue; } - out.pipeline = CreateFxPipeline(device, target_format, out.pipeline_layout, + out.renderpass = CreateFxRenderPass(device, target_format, out.clear); + out.pipeline = CreateFxPipeline(device, out.renderpass, out.pipeline_layout, *vertex_shader->second, *fragment_shader->second, pass); + if (out.writes_backbuffer) { + out.backbuffer_slot = static_cast(effect.backbuffer_pass_count % 2); + ++effect.backbuffer_pass_count; + for (u32 image = 0; image < m_image_count; ++image) { + for (size_t slot = 0; slot < 2; ++slot) { + out.framebuffers.push_back(CreateWrappedFramebuffer( + device, out.renderpass, m_frames[image].views[slot], out.extent)); + } + } + } else { + out.framebuffers.push_back( + CreateWrappedFramebuffer(device, out.renderpass, + effect.textures[out.target_texture].view, out.extent)); + } + effect.passes.push_back(std::move(out)); } @@ -787,20 +851,19 @@ void PostProcessChain::Draw(const Device& device, Scheduler& scheduler, size_t i UpdateDescriptors(device, effect, pass, image_index, current_view); + VkFramebuffer framebuffer{}; VkImage target_image{}; - VkImageView target_view{}; if (pass.writes_backbuffer) { const u32 target_slot = (slot + 1) % 2; + framebuffer = *pass.framebuffers[image_index * 2 + target_slot]; target_image = *frame.images[target_slot]; - target_view = *frame.views[target_slot]; } else { - const Texture& texture = effect.textures[pass.target_texture]; - target_image = *texture.image; - target_view = *texture.view; + framebuffer = *pass.framebuffers[0]; + target_image = *effect.textures[pass.target_texture].image; } const VkImage source_image = current_image; - const VkAttachmentLoadOp load_op = pass.load_op; + const VkRenderPass renderpass = *pass.renderpass; const VkPipeline pipeline = *pass.pipeline; const VkPipelineLayout layout = *pass.pipeline_layout; const VkDescriptorSet uniform_set = effect.uniform_sets[image_index]; @@ -812,12 +875,12 @@ void PostProcessChain::Draw(const Device& device, Scheduler& scheduler, size_t i scheduler.Record([=](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); TransitionImageLayout(cmdbuf, target_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRendering(cmdbuf, target_view, extent, load_op); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, std::array{uniform_set, sampler_set}, {}); cmdbuf.Draw(vertices, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, target_image, VK_IMAGE_LAYOUT_GENERAL); }); diff --git a/src/video_core/renderer_vulkan/present/post_process.h b/src/video_core/renderer_vulkan/present/post_process.h index cc65cfe172..9d42bd4751 100644 --- a/src/video_core/renderer_vulkan/present/post_process.h +++ b/src/video_core/renderer_vulkan/present/post_process.h @@ -77,16 +77,19 @@ private: }; struct Pass { + vk::RenderPass renderpass{}; vk::Pipeline pipeline{}; vk::DescriptorSetLayout sampler_layout{}; vk::PipelineLayout pipeline_layout{}; vk::DescriptorSets sampler_sets{}; std::vector sampler_bindings{}; + std::vector framebuffers{}; size_t target_texture{}; VkExtent2D extent{}; u32 num_vertices{3}; - VkAttachmentLoadOp load_op{VK_ATTACHMENT_LOAD_OP_LOAD}; + bool clear{}; bool writes_backbuffer{}; + u32 backbuffer_slot{}; }; struct Effect { @@ -102,6 +105,8 @@ private: vk::DescriptorSetLayout uniform_layout{}; vk::DescriptorPool descriptor_pool{}; vk::DescriptorSets uniform_sets{}; + size_t backbuffer_pass_count{}; + u32 backbuffer_slots{1}; }; struct FrameImages { diff --git a/src/video_core/renderer_vulkan/present/sgsr.cpp b/src/video_core/renderer_vulkan/present/sgsr.cpp index 1787b43e80..fa37225e43 100644 --- a/src/video_core/renderer_vulkan/present/sgsr.cpp +++ b/src/video_core/renderer_vulkan/present/sgsr.cpp @@ -27,8 +27,16 @@ SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image , m_extent{extent} , m_edge_dir{edge_dir} { - m_image = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - m_image_view = CreateWrappedImageView(device, m_image, VK_FORMAT_R16G16B16A16_SFLOAT); + // Not finished yet initializing at ctor time? + m_dynamic_images.resize(m_image_count); + for (auto& images : m_dynamic_images) { + images.image = CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_view = CreateWrappedImageView(device, images.image, VK_FORMAT_R16G16B16A16_SFLOAT); + } + + m_renderpass = CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + for (auto& images : m_dynamic_images) + images.framebuffer = CreateWrappedFramebuffer(device, m_renderpass, images.image_view, m_extent); m_sampler = CreateBilinearSampler(device); m_vert_shader = BuildShader(device, SGSR1_SHADER_VERT_SPV); @@ -40,8 +48,8 @@ SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image m_descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); VkDescriptorSetLayout layout = *m_descriptor_set_layout; - for (size_t i = 0; i < m_image_count; ++i) - m_descriptor_sets.push_back(CreateWrappedDescriptorSets(m_descriptor_pool, layout)); + for (auto& images : m_dynamic_images) + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layout); const VkPushConstantRange range{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, @@ -58,26 +66,38 @@ SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image .pPushConstantRanges = &range, }; m_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); - m_stage_pipeline = CreateWrappedPipeline(device, VK_FORMAT_R16G16B16A16_SFLOAT, - m_pipeline_layout, - std::tie(m_vert_shader, m_stage_shader)); + m_stage_pipeline = CreateWrappedPipeline(device, m_renderpass, m_pipeline_layout, std::tie(m_vert_shader, m_stage_shader)); } void SGSR::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; std::vector image_infos; std::vector updates; image_infos.reserve(1); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, m_descriptor_sets[image_index][0], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, images.descriptor_sets[0], 0)); device.GetLogical().UpdateDescriptorSets(updates, {}); } +void SGSR::UploadImages(const Device& device, Scheduler& scheduler) { + if (!m_images_ready) { + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) + ClearColorImage(cmdbuf, *image.image); + }); + scheduler.Finish(); + m_images_ready = true; + } +} + VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect) { - auto const output_image = *m_image; - auto const output_view = *m_image_view; - auto const descriptor_set = m_descriptor_sets[image_index][0]; + Images& images = m_dynamic_images[image_index]; + auto const output_image = *images.image; + auto const descriptor_set = images.descriptor_sets[0]; + auto const framebuffer = *images.framebuffer; auto const pipeline = *m_stage_pipeline; VkPipelineLayout layout = *m_pipeline_layout; + VkRenderPass renderpass = *m_renderpass; VkExtent2D extent = m_extent; const f32 input_image_width = f32(input_image_extent.width); @@ -105,22 +125,22 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_ viewport_con[7] = std::bit_cast((std::min)(crop_rect.top, crop_rect.bottom)); viewport_con[8] = std::bit_cast(edge_sharpness); + UploadImages(device, scheduler); UpdateDescriptorSets(device, source_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, output_view, extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, viewport_con); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); }); - return output_view; + return *images.image_view; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/sgsr.h b/src/video_core/renderer_vulkan/present/sgsr.h index 52a645b09f..42ba00898f 100644 --- a/src/video_core/renderer_vulkan/present/sgsr.h +++ b/src/video_core/renderer_vulkan/present/sgsr.h @@ -19,6 +19,7 @@ public: VkImageView Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage source_image, VkImageView source_image_view, VkExtent2D input_image_extent, const Common::Rectangle& crop_rect); private: void Initialize(const Device& device); + void UploadImages(const Device& device, Scheduler& scheduler); void UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index); MemoryAllocator& m_memory_allocator; @@ -31,11 +32,17 @@ private: vk::ShaderModule m_vert_shader; vk::ShaderModule m_stage_shader; vk::Pipeline m_stage_pipeline; + vk::RenderPass m_renderpass; vk::Sampler m_sampler; - vk::Image m_image; - vk::ImageView m_image_view; - std::vector m_descriptor_sets; + struct Images { + vk::DescriptorSets descriptor_sets; + vk::Image image; + vk::ImageView image_view; + vk::Framebuffer framebuffer; + }; + std::vector m_dynamic_images; + bool m_images_ready{}; bool m_edge_dir{}; }; diff --git a/src/video_core/renderer_vulkan/present/smaa.cpp b/src/video_core/renderer_vulkan/present/smaa.cpp index e4258d4b5c..28b0427fa0 100644 --- a/src/video_core/renderer_vulkan/present/smaa.cpp +++ b/src/video_core/renderer_vulkan/present/smaa.cpp @@ -32,6 +32,7 @@ SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, , m_image_count(u32(image_count)) { CreateImages(device); + CreateRenderPasses(device); CreateSampler(device); CreateShaders(device); CreateDescriptorPool(device); @@ -55,18 +56,42 @@ void SMAA::CreateImages(const Device& device) { m_static_image_views[Search] = CreateWrappedImageView(device, m_static_images[Search], VK_FORMAT_R8_UNORM); - m_dynamic_images[Blend] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - m_dynamic_images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); - m_dynamic_images[Output] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + for (u32 i = 0; i < m_image_count; i++) { + Images& images = m_dynamic_images.emplace_back(); - m_dynamic_image_views[Blend] = - CreateWrappedImageView(device, m_dynamic_images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); - m_dynamic_image_views[Edges] = - CreateWrappedImageView(device, m_dynamic_images[Edges], VK_FORMAT_R16G16_SFLOAT); - m_dynamic_image_views[Output] = - CreateWrappedImageView(device, m_dynamic_images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Blend] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); + images.images[Output] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + + images.image_views[Blend] = + CreateWrappedImageView(device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Edges] = + CreateWrappedImageView(device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); + images.image_views[Output] = + CreateWrappedImageView(device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void SMAA::CreateRenderPasses(const Device& device) { + m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(device, VK_FORMAT_R16G16_SFLOAT); + m_renderpasses[BlendingWeightCalculation] = + CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + m_renderpasses[NeighborhoodBlending] = + CreateWrappedRenderPass(device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( + device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); + + images.framebuffers[BlendingWeightCalculation] = + CreateWrappedFramebuffer(device, m_renderpasses[BlendingWeightCalculation], + images.image_views[Blend], m_extent); + + images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( + device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); + } } void SMAA::CreateSampler(const Device& device) { @@ -118,8 +143,8 @@ void SMAA::CreateDescriptorSets(const Device& device) { std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), [](auto& layout) { return *layout; }); - for (u32 i = 0; i < m_image_count; i++) { - m_descriptor_sets.push_back(CreateWrappedDescriptorSets(m_descriptor_pool, layouts)); + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); } } @@ -130,43 +155,36 @@ void SMAA::CreatePipelineLayouts(const Device& device) { } void SMAA::CreatePipelines(const Device& device) { - static constexpr std::array formats{ - VK_FORMAT_R16G16_SFLOAT, - VK_FORMAT_R16G16B16A16_SFLOAT, - VK_FORMAT_R16G16B16A16_SFLOAT, - }; for (size_t i = 0; i < MaxSMAAStage; i++) { m_pipelines[i] = - CreateWrappedPipeline(device, formats[i], m_pipeline_layouts[i], + CreateWrappedPipeline(device, m_renderpasses[i], m_pipeline_layouts[i], std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); } } void SMAA::UpdateDescriptorSets(const Device& device, VkImageView image_view, size_t image_index) { - const vk::DescriptorSets& descriptor_sets = m_descriptor_sets[image_index]; + Images& images = m_dynamic_images[image_index]; std::vector image_infos; std::vector updates; image_infos.reserve(6); updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - descriptor_sets[EdgeDetection], 0)); + images.descriptor_sets[EdgeDetection], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, - *m_dynamic_image_views[Edges], - descriptor_sets[BlendingWeightCalculation], + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], + images.descriptor_sets[BlendingWeightCalculation], 0)); updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], - descriptor_sets[BlendingWeightCalculation], + images.descriptor_sets[BlendingWeightCalculation], 1)); updates.push_back( CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], - descriptor_sets[BlendingWeightCalculation], 2)); + images.descriptor_sets[BlendingWeightCalculation], 2)); updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - descriptor_sets[NeighborhoodBlending], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, - *m_dynamic_image_views[Blend], - descriptor_sets[NeighborhoodBlending], 1)); + images.descriptor_sets[NeighborhoodBlending], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], + images.descriptor_sets[NeighborhoodBlending], 1)); device.GetLogical().UpdateDescriptorSets(updates, {}); } @@ -184,26 +202,36 @@ void SMAA::UploadImages(const Device& device, Scheduler& scheduler) { UploadImage(device, m_allocator, scheduler, m_static_images[Search], search_extent, VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& images : m_dynamic_images) { + for (size_t i = 0; i < MaxDynamicImage; i++) { + ClearColorImage(cmdbuf, *images.images[i]); + } + } + }); + scheduler.Finish(); + m_images_ready = true; } void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image, VkImageView* inout_image_view) { - const vk::DescriptorSets& descriptor_sets = m_descriptor_sets[image_index]; + Images& images = m_dynamic_images[image_index]; VkImage input_image = *inout_image; - VkImage output_image = *m_dynamic_images[Output]; - VkImage edges_image = *m_dynamic_images[Edges]; - VkImage blend_image = *m_dynamic_images[Blend]; + VkImage output_image = *images.images[Output]; + VkImage edges_image = *images.images[Edges]; + VkImage blend_image = *images.images[Blend]; - VkDescriptorSet edge_detection_descriptor_set = descriptor_sets[EdgeDetection]; + VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; VkDescriptorSet blending_weight_calculation_descriptor_set = - descriptor_sets[BlendingWeightCalculation]; + images.descriptor_sets[BlendingWeightCalculation]; VkDescriptorSet neighborhood_blending_descriptor_set = - descriptor_sets[NeighborhoodBlending]; + images.descriptor_sets[NeighborhoodBlending]; - VkImageView edges_view = *m_dynamic_image_views[Edges]; - VkImageView blend_view = *m_dynamic_image_views[Blend]; - VkImageView output_view = *m_dynamic_image_views[Output]; + VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; + VkFramebuffer blending_weight_calculation_framebuffer = + *images.framebuffers[BlendingWeightCalculation]; + VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; UploadImages(device, scheduler); UpdateDescriptorSets(device, *inout_image_view, image_index); @@ -211,43 +239,43 @@ void SMAA::Draw(const Device& device, Scheduler& scheduler, size_t image_index, scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, edges_view, m_extent, VK_ATTACHMENT_LOAD_OP_CLEAR); + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer, + m_extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline_layouts[EdgeDetection], 0, edge_detection_descriptor_set, {}); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, blend_view, m_extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation], + blending_weight_calculation_framebuffer, m_extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[BlendingWeightCalculation]); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline_layouts[BlendingWeightCalculation], 0, blending_weight_calculation_descriptor_set, {}); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, output_view, m_extent, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending], + neighborhood_blending_framebuffer, m_extent); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline_layouts[NeighborhoodBlending], 0, neighborhood_blending_descriptor_set, {}); cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); }); - *inout_image = *m_dynamic_images[Output]; - *inout_image_view = *m_dynamic_image_views[Output]; + *inout_image = *images.images[Output]; + *inout_image_view = *images.image_views[Output]; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/smaa.h b/src/video_core/renderer_vulkan/present/smaa.h index f9c87c9c6a..a547161395 100644 --- a/src/video_core/renderer_vulkan/present/smaa.h +++ b/src/video_core/renderer_vulkan/present/smaa.h @@ -47,6 +47,7 @@ private: }; void CreateImages(const Device& device); + void CreateRenderPasses(const Device& device); void CreateSampler(const Device& device); void CreateShaders(const Device& device); void CreateDescriptorPool(const Device& device); @@ -67,13 +68,18 @@ private: std::array m_vertex_shaders{}; std::array m_fragment_shaders{}; std::array m_pipelines{}; + std::array m_renderpasses{}; std::array m_static_images{}; std::array m_static_image_views{}; - std::array m_dynamic_images{}; - std::array m_dynamic_image_views{}; - std::vector m_descriptor_sets{}; + struct Images { + vk::DescriptorSets descriptor_sets{}; + std::array images{}; + std::array image_views{}; + std::array framebuffers{}; + }; + std::vector m_dynamic_images{}; bool m_images_ready{}; vk::Sampler m_sampler{}; diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 63e8e5f5e8..2ea7870f01 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -49,13 +49,13 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, VkImageLayout source_layout) { - const VkImageMemoryBarrier2 barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = flags, + .dstAccessMask = flags, .oldLayout = source_layout, .newLayout = target_layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -69,7 +69,8 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo .layerCount = 1, }, }; - cmdbuf.PipelineBarrier(barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, barrier); } void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, @@ -89,9 +90,7 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc std::ranges::copy(initial_contents, upload_buffer.Mapped().begin()); upload_buffer.Flush(); - const VkBufferImageCopy2 region{ - .sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .pNext = nullptr, + const std::array regions{{{ .bufferOffset = 0, .bufferRowLength = dimensions.width, .bufferImageHeight = dimensions.height, @@ -101,14 +100,14 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc .layerCount = 1}, .imageOffset{}, .imageExtent{.width = dimensions.width, .height = dimensions.height, .depth = 1}, - }; + }}}; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([&](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED); cmdbuf.CopyBufferToImage(*upload_buffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - region); + regions); TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); }); @@ -117,13 +116,11 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, VkExtent3D extent) { - const VkImageMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -137,13 +134,11 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - const VkImageMemoryBarrier2 image_write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -157,17 +152,13 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - static constexpr VkMemoryBarrier2 memory_write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_HOST, - .dstAccessMask = VK_ACCESS_2_HOST_READ_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - const VkBufferImageCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .pNext = nullptr, + const VkBufferImageCopy copy{ .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, @@ -180,9 +171,11 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe .imageOffset{.x = 0, .y = 0, .z = 0}, .imageExtent{extent}, }; - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); - cmdbuf.PipelineBarrier(0, memory_write_barrier, {}, image_write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, + memory_write_barrier, nullptr, image_write_barrier); } vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { @@ -202,6 +195,77 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF }); } +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout) { + const VkAttachmentDescription attachment{ + .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, + .format = format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = initial_layout, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + constexpr VkAttachmentReference color_attachment_ref{ + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkSubpassDescription subpass_description{ + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = 1, + .pColorAttachments = &color_attachment_ref, + .pResolveAttachments = nullptr, + .pDepthStencilAttachment = nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + + constexpr VkSubpassDependency dependency{ + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dependencyFlags = 0, + }; + + return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .attachmentCount = 1, + .pAttachments = &attachment, + .subpassCount = 1, + .pSubpasses = &subpass_description, + .dependencyCount = 1, + .pDependencies = &dependency, + }); +} + +vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, + vk::ImageView& dest_image, VkExtent2D extent) { + return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = *render_pass, + .attachmentCount = 1, + .pAttachments = dest_image.address(), + .width = extent.width, + .height = extent.height, + .layers = 1, + }); +} + vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, @@ -332,20 +396,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, }); } -VkPipelineRenderingCreateInfo ColorRenderingInfo(const VkFormat& format) { - return { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, - .pNext = nullptr, - .viewMask = 0, - .colorAttachmentCount = 1, - .pColorAttachmentFormats = &format, - .depthAttachmentFormat = VK_FORMAT_UNDEFINED, - .stencilAttachmentFormat = VK_FORMAT_UNDEFINED, - }; -} - static vk::Pipeline CreateWrappedPipelineImpl( - const Device& device, VkFormat format, vk::PipelineLayout& layout, + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders, VkPipelineColorBlendAttachmentState blending) { const std::array shader_stages{{ @@ -449,10 +501,9 @@ static vk::Pipeline CreateWrappedPipelineImpl( .pDynamicStates = dynamic_states.data(), }; - const VkPipelineRenderingCreateInfo rendering_ci = ColorRenderingInfo(format); return device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = 0, .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), @@ -466,14 +517,14 @@ static vk::Pipeline CreateWrappedPipelineImpl( .pColorBlendState = &color_blend_ci, .pDynamicState = &dynamic_state_ci, .layout = *layout, - .renderPass = VK_NULL_HANDLE, + .renderPass = *renderpass, .subpass = 0, .basePipelineHandle = 0, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } -vk::Pipeline CreateWrappedPipeline(const Device& device, VkFormat format, +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders) { constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ @@ -488,12 +539,12 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, VkFormat format, VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; - return CreateWrappedPipelineImpl(device, format, layout, shaders, + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, color_blend_attachment_disabled); } vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( - const Device& device, VkFormat format, vk::PipelineLayout& layout, + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders) { constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{ .blendEnable = VK_TRUE, @@ -507,12 +558,12 @@ vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; - return CreateWrappedPipelineImpl(device, format, layout, shaders, + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, color_blend_attachment_premultiplied); } vk::Pipeline CreateWrappedCoverageBlendingPipeline( - const Device& device, VkFormat format, vk::PipelineLayout& layout, + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders) { constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{ .blendEnable = VK_TRUE, @@ -526,7 +577,7 @@ vk::Pipeline CreateWrappedCoverageBlendingPipeline( VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; - return CreateWrappedPipelineImpl(device, format, layout, shaders, + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, color_blend_attachment_coverage); } @@ -648,35 +699,21 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); } -void BeginRendering(vk::CommandBuffer& cmdbuf, VkImageView view, VkExtent2D extent, - VkAttachmentLoadOp load_op, const VkClearValue& clear_value) { - const VkRenderingAttachmentInfo attachment{ - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent) { + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, - .imageView = view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = VK_NULL_HANDLE, - .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .loadOp = load_op, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = clear_value, - }; - cmdbuf.BeginRendering(VkRenderingInfo{ - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .pNext = nullptr, - .flags = 0, + .renderPass = render_pass, + .framebuffer = framebuffer, .renderArea{ .offset{}, .extent = extent, }, - .layerCount = 1, - .viewMask = 0, - .colorAttachmentCount = 1, - .pColorAttachments = &attachment, - .pDepthAttachment = nullptr, - .pStencilAttachment = nullptr, - }); + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); const VkViewport viewport{ .x = 0.0f, diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 9eb862b0a8..ecc263286d 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -27,6 +27,10 @@ void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffe void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL); +vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, + vk::ImageView& dest_image, VkExtent2D extent); vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span code); vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, @@ -43,17 +47,16 @@ vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool, vk::Span layouts); vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, vk::DescriptorSetLayout& layout); -[[nodiscard]] VkPipelineRenderingCreateInfo ColorRenderingInfo(const VkFormat& format); -vk::Pipeline CreateWrappedPipeline(const Device& device, VkFormat format, +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders); vk::Pipeline CreateWrappedComputePipeline(const Device& device, vk::PipelineLayout& layout, VkShaderModule shader); vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( - const Device& device, VkFormat format, vk::PipelineLayout& layout, + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders); vk::Pipeline CreateWrappedCoverageBlendingPipeline( - const Device& device, VkFormat format, vk::PipelineLayout& layout, + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, std::tuple shaders); VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector& images, VkSampler sampler, VkImageView view, @@ -62,7 +65,7 @@ vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights); -void BeginRendering(vk::CommandBuffer& cmdbuf, VkImageView view, VkExtent2D extent, - VkAttachmentLoadOp load_op, const VkClearValue& clear_value = {}); +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent); } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index 9cd99206c7..549b0660c1 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -25,7 +25,8 @@ WindowAdaptPass::WindowAdaptPass(const Device& device, VkFormat frame_format, vk CreateDescriptorSetLayout(device); CreatePipelineLayout(device); CreateVertexShader(device); - CreatePipelines(device, frame_format); + CreateRenderPass(device, frame_format); + CreatePipelines(device); } WindowAdaptPass::~WindowAdaptPass() = default; @@ -34,70 +35,66 @@ void WindowAdaptPass::Draw(const Device& device, RasterizerVulkan& rasterizer, S std::list& layers, std::span configs, const Layout::FramebufferLayout& layout, Frame* dst) { + + const VkFramebuffer host_framebuffer{*dst->framebuffer}; + const VkRenderPass renderpass{*render_pass}; + const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; + const VkExtent2D render_area{ + .width = dst->width, + .height = dst->height, + }; + const size_t layer_count = configs.size(); - layer_push_constants.resize(layer_count); - layer_descriptor_sets.resize(layer_count); - layer_pipelines.resize(layer_count); + std::vector push_constants(layer_count); + std::vector descriptor_sets(layer_count); + std::vector graphics_pipelines(layer_count); auto layer_it = layers.begin(); for (size_t i = 0; i < layer_count; i++) { switch (configs[i].blending) { case Tegra::BlendMode::Opaque: default: - layer_pipelines[i] = *opaque_pipeline; + graphics_pipelines[i] = *opaque_pipeline; break; case Tegra::BlendMode::Premultiplied: - layer_pipelines[i] = *premultiplied_pipeline; + graphics_pipelines[i] = *premultiplied_pipeline; break; case Tegra::BlendMode::Coverage: - layer_pipelines[i] = *coverage_pipeline; + graphics_pipelines[i] = *coverage_pipeline; break; } - layer_it->ConfigureDraw(device, &layer_push_constants[i], &layer_descriptor_sets[i], - rasterizer, *sampler, image_index, configs[i], layout); + layer_it->ConfigureDraw(device, &push_constants[i], &descriptor_sets[i], rasterizer, *sampler, + image_index, configs[i], layout); layer_it++; } - Record(scheduler, dst, layer_push_constants, layer_descriptor_sets); -} - -void WindowAdaptPass::DrawGenerated(const Device& device, Scheduler& scheduler, Layer& layer, - size_t generation, VkImage image, VkImageView view, - const Layout::FramebufferLayout& layout, Frame* dst) { - std::vector push_constants = layer_push_constants; - std::vector descriptor_sets = layer_descriptor_sets; - layer.ConfigureGenerated(device, &push_constants.front(), &descriptor_sets.front(), *sampler, - generation, image, view, layout); - Record(scheduler, dst, std::move(push_constants), std::move(descriptor_sets)); -} - -void WindowAdaptPass::Record(Scheduler& scheduler, Frame* dst, - std::vector push_constants, - std::vector descriptor_sets) const { - const VkImage host_image{*dst->image}; - const VkImageView host_view{*dst->image_view}; - const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; - const VkExtent2D render_area{ - .width = dst->width, - .height = dst->height, - }; - scheduler.Record([host_image, host_view, graphics_pipeline_layout, render_area, - graphics_pipelines = layer_pipelines, - push_constants = std::move(push_constants), - descriptor_sets = std::move(descriptor_sets)](vk::CommandBuffer cmdbuf) { + scheduler.Record([=](vk::CommandBuffer cmdbuf) { const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; - const VkClearValue clear_value{ - .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + const VkClearAttachment clear_attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = 0, + .clearValue = + { + .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + }, + }; + const VkClearRect clear_rect{ + .rect = + { + .offset = {0, 0}, + .extent = render_area, + }, + .baseArrayLayer = 0, + .layerCount = 1, }; - TransitionImageLayout(cmdbuf, host_image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - BeginRendering(cmdbuf, host_view, render_area, VK_ATTACHMENT_LOAD_OP_CLEAR, clear_value); + BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); + cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); - for (size_t i = 0; i < graphics_pipelines.size(); i++) { + for (size_t i = 0; i < layer_count; i++) { cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]); cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants[i]); @@ -106,7 +103,7 @@ void WindowAdaptPass::Record(Scheduler& scheduler, Frame* dst, cmdbuf.Draw(4, 1, 0, 0); } - cmdbuf.EndRendering(); + cmdbuf.EndRenderPass(); }); } @@ -114,6 +111,10 @@ VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { return *descriptor_set_layout; } +VkRenderPass WindowAdaptPass::GetRenderPass() { + return *render_pass; +} + void WindowAdaptPass::CreateDescriptorSetLayout(const Device& device) { descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); @@ -141,13 +142,17 @@ void WindowAdaptPass::CreateVertexShader(const Device& device) { vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); } -void WindowAdaptPass::CreatePipelines(const Device& device, VkFormat frame_format) { - opaque_pipeline = CreateWrappedPipeline(device, frame_format, pipeline_layout, +void WindowAdaptPass::CreateRenderPass(const Device& device, VkFormat frame_format) { + render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void WindowAdaptPass::CreatePipelines(const Device& device) { + opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline( - device, frame_format, pipeline_layout, std::tie(vertex_shader, fragment_shader)); + device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); coverage_pipeline = CreateWrappedCoverageBlendingPipeline( - device, frame_format, pipeline_layout, std::tie(vertex_shader, fragment_shader)); + device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h index d33cacaee9..58d6fc01c0 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h @@ -7,10 +7,8 @@ #pragma once #include -#include #include "common/math_util.h" -#include "video_core/renderer_vulkan/present/present_push_constants.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Layout { @@ -39,32 +37,25 @@ public: std::list& layers, std::span configs, const Layout::FramebufferLayout& layout, Frame* dst); - void DrawGenerated(const Device& device, Scheduler& scheduler, Layer& layer, size_t generation, - VkImage image, VkImageView view, const Layout::FramebufferLayout& layout, - Frame* dst); - VkDescriptorSetLayout GetDescriptorSetLayout(); + VkRenderPass GetRenderPass(); private: - void Record(Scheduler& scheduler, Frame* dst, std::vector push_constants, - std::vector descriptor_sets) const; - void CreateDescriptorSetLayout(const Device& device); void CreatePipelineLayout(const Device& device); void CreateVertexShader(const Device& device); - void CreatePipelines(const Device& device, VkFormat frame_format); + void CreateRenderPass(const Device& device, VkFormat frame_format); + void CreatePipelines(const Device& device); vk::DescriptorSetLayout descriptor_set_layout; vk::PipelineLayout pipeline_layout; vk::Sampler sampler; vk::ShaderModule vertex_shader; vk::ShaderModule fragment_shader; + vk::RenderPass render_pass; vk::Pipeline opaque_pipeline; vk::Pipeline premultiplied_pipeline; vk::Pipeline coverage_pipeline; - std::vector layer_pipelines; - std::vector layer_push_constants; - std::vector layer_descriptor_sets; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index b207997b76..27ca531186 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -52,6 +52,23 @@ constexpr VkExtent2D CaptureImageSize{ .height = VideoCore::Capture::LinearHeight, }; +#ifdef HAS_LSFG +[[nodiscard]] VkExtent2D GuestExtent(std::span framebuffers) { + if (framebuffers.empty()) { + return VkExtent2D{}; + } + + const auto& framebuffer = framebuffers.front(); + if (framebuffer.crop_rect.IsEmpty()) { + return VkExtent2D{.width = framebuffer.width, .height = framebuffer.height}; + } + return VkExtent2D{ + .width = static_cast(framebuffer.crop_rect.GetWidth()), + .height = static_cast(framebuffer.crop_rect.GetHeight()), + }; +} +#endif + constexpr VkExtent3D CaptureImageExtent{ .width = VideoCore::Capture::LinearWidth, .height = VideoCore::Capture::LinearHeight, @@ -185,9 +202,8 @@ RendererVulkan::~RendererVulkan() { } void RendererVulkan::Composite(std::span framebuffers) { - u32 pace_index = 0; SCOPE_EXIT { - render_window.OnFrameDisplayed(pace_index + 1); + render_window.OnFrameDisplayed(); }; RenderAppletCaptureLayer(framebuffers); @@ -205,38 +221,25 @@ void RendererVulkan::Composite(std::span framebu present_manager.SwapchainImageCount(), swapchain.GetImageViewFormat()); - std::chrono::nanoseconds pace_step{}; #ifdef HAS_LSFG void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames())); - const FrameGenSource source = blit_swapchain.GenerationSource(); - frame_gen.Process(device, source.view, source.extent); + frame_gen.Process(device, frame, present_manager.SwapchainImageFormat(), + GuestExtent(framebuffers)); - const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); const size_t generated_frames = frame_gen.GeneratedFrameCount(); - const std::chrono::nanoseconds generated_step = frame_gen.PaceStep(); for (size_t generation = 0; generation < generated_frames; ++generation) { - if (!blit_swapchain.IsGenerationFree(generation)) { - break; - } - Frame* const generated = present_manager.TryGetRenderFrame(); - if (generated == nullptr) { - break; - } - blit_swapchain.PrepareFrame(device, generated, layout); - const LsfgImage& output = frame_gen.Generate(device, generation); - blit_swapchain.DrawGenerated(device, generated, layout, generation, output.Handle(), - output.View()); + Frame* generated = present_manager.GetRenderFrame(); + blit_swapchain.PrepareFrame(device, generated, render_window.GetFramebufferLayout()); + frame_gen.GenerateInto(device, generated, generation); scheduler.Flush(*generated->render_ready); - present_manager.Present(generated, static_cast(generation), generated_step); - pace_index = static_cast(generation + 1); - pace_step = generated_step; + present_manager.Present(generated); } #endif scheduler.Flush(*frame->render_ready); - present_manager.Present(frame, pace_index, pace_step); + present_manager.Present(frame); #ifdef HAS_LSFG scheduler.DispatchWork(); #endif @@ -272,6 +275,7 @@ vk::Buffer RendererVulkan::RenderToBuffer(std::span framebuffers, diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 1654717a36..205382f81a 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -33,8 +33,8 @@ BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const BlitScreen::~BlitScreen() = default; void BlitScreen::WaitIdle(const Device& device) { - scheduler.Finish(); present_manager.WaitPresent(); + scheduler.Finish(); device.GetLogical().WaitIdle(); } @@ -92,33 +92,14 @@ void BlitScreen::PrepareFrame(const Device& device, Frame* frame, return; } - if (frame->width == layout.width && frame->height == layout.height) { + if (frame->width != layout.width || frame->height != layout.height) { + WaitIdle(device); + } else if (!present_manager.NeedsStorage(frame, true)) { return; } - WaitIdle(device); - present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format); -} - -FrameGenSource BlitScreen::GenerationSource() const { - if (layers.empty()) { - return {}; - } - return FrameGenSource{ - .view = layers.front().GenerationSource(), - .extent = layers.front().GenerationExtent(), - }; -} - -bool BlitScreen::IsGenerationFree(size_t generation) const { - return !layers.empty() && layers.front().IsGenerationFree(generation); -} - -void BlitScreen::DrawGenerated(const Device& device, Frame* frame, - const Layout::FramebufferLayout& layout, size_t generation, - VkImage image, VkImageView view) { - window_adapt->DrawGenerated(device, scheduler, layers.front(), generation, image, view, layout, - frame); + present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, + window_adapt->GetRenderPass(), true); } void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, Frame* frame, @@ -145,16 +126,20 @@ void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, swapchain_view_format = current_swapchain_view_format; } + const bool storage_required = Settings::values.frame_gen.GetValue(); if (resource_update_required) { WaitIdle(device); SetWindowAdaptPass(device); if (presentation_recreate_required) { - present_manager.RecreateFrame(frame, layout.width, layout.height, - swapchain_view_format); + present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, + window_adapt->GetRenderPass(), storage_required); } image_index = 0; + } else if (present_manager.NeedsStorage(frame, storage_required)) { + present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, + window_adapt->GetRenderPass(), true); } const VkExtent2D window_size{ @@ -162,14 +147,11 @@ void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, .height = layout.screen.GetHeight(), }; - const size_t generations = Settings::FrameGenMaxGenerations(); - if (layers.size() != framebuffers.size() || generation_count != generations) { + if (layers.size() != framebuffers.size()) { layers.clear(); - generation_count = generations; for (size_t i = 0; i < framebuffers.size(); ++i) { layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, - generation_count, window_size, - window_adapt->GetDescriptorSetLayout(), filters); + window_size, window_adapt->GetDescriptorSetLayout(), filters); } } @@ -180,4 +162,39 @@ void BlitScreen::DrawToFrame(const Device& device, RasterizerVulkan& rasterizer, } } +vk::Framebuffer BlitScreen::CreateFramebuffer(const Device& device, const Layout::FramebufferLayout& layout, + VkImageView image_view, + VkFormat current_view_format) { + bool format_updated = swapchain_view_format != current_view_format; + swapchain_view_format = current_view_format; + + if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) { + WaitIdle(device); + SetWindowAdaptPass(device); + image_index = 0; + } + + const VkExtent2D extent{ + .width = layout.width, + .height = layout.height, + }; + + return CreateFramebuffer(device, image_view, extent, window_adapt->GetRenderPass()); +} + +vk::Framebuffer BlitScreen::CreateFramebuffer(const Device& device, const VkImageView& image_view, VkExtent2D extent, + VkRenderPass render_pass) { + return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = render_pass, + .attachmentCount = 1, + .pAttachments = &image_view, + .width = extent.width, + .height = extent.height, + .layers = 1, + }); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index c6cac52321..56b429ae53 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -48,11 +48,6 @@ struct FramebufferTextureInfo { u32 scaled_height{}; }; -struct FrameGenSource { - VkImageView view{}; - VkExtent2D extent{}; -}; - class BlitScreen { public: explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, @@ -67,16 +62,15 @@ public: void PrepareFrame(const Device& device, Frame* frame, const Layout::FramebufferLayout& layout); - [[nodiscard]] FrameGenSource GenerationSource() const; - - [[nodiscard]] bool IsGenerationFree(size_t generation) const; - - void DrawGenerated(const Device& device, Frame* frame, const Layout::FramebufferLayout& layout, - size_t generation, VkImage image, VkImageView view); + [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Device& device, const Layout::FramebufferLayout& layout, + VkImageView image_view, + VkFormat current_view_format); private: void WaitIdle(const Device& device); void SetWindowAdaptPass(const Device& device); + vk::Framebuffer CreateFramebuffer(const Device& device, const VkImageView& image_view, VkExtent2D extent, + VkRenderPass render_pass); Tegra::MaxwellDeviceMemoryManager& device_memory; MemoryAllocator& memory_allocator; @@ -85,7 +79,6 @@ private: const PresentFilters& filters; std::size_t image_count{}; std::size_t image_index{}; - std::size_t generation_count{}; VkFormat swapchain_view_format{}; Settings::ScalingFilter scaling_filter{}; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 49926d01ad..493de2cc20 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -24,31 +24,8 @@ namespace Vulkan { namespace { -constexpr u32 COMPACT_VERTEX_BINDINGS = 8; - -constexpr VkMemoryBarrier2 HOST_READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, - .dstAccessMask = VK_ACCESS_2_HOST_READ_BIT, -}; - -template -struct VertexBindings { - std::array buffers; - std::array offsets; - std::array sizes; - std::array strides; - u32 first; - u32 count; -}; - -VkBufferCopy2 MakeBufferCopy(const VideoCommon::BufferCopy& copy) { - return VkBufferCopy2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, +VkBufferCopy MakeBufferCopy(const VideoCommon::BufferCopy& copy) { + return VkBufferCopy{ .srcOffset = copy.src_offset, .dstOffset = copy.dst_offset, .size = copy.size, @@ -106,20 +83,15 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, }; - MemoryUsage usage = MemoryUsage::DeviceLocal; - if (device.IsUMA()) { - usage = MemoryUsage::Stream; - } if (sparse_alignment > 1) { - return memory_allocator.CreateBuffer(buffer_ci, usage, sparse_alignment); + return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal, sparse_alignment); } - return memory_allocator.CreateBuffer(buffer_ci, usage); + return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal); } } // Anonymous namespace Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params) - : VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096}, - uploads{4096} { + : VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096} { if (runtime.device.HasNullDescriptor()) { return; } @@ -137,7 +109,7 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_, scheduler{&runtime.scheduler}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes(), runtime.SparseAlignmentFor(sparse_compatible_))}, - tracker{SizeBytes()}, uploads{SizeBytes()} { + tracker{SizeBytes()} { sparse_compatible = sparse_compatible_; if (runtime.device.HasDebuggingToolAttached()) { buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str()); @@ -147,38 +119,9 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_, } } -bool Buffer::IsRegionUsed(u64 offset, u64 size) const noexcept { - return tracker.IsUsed(offset, size, scheduler->GetMasterSemaphore().KnownGpuTick()); -} - -bool Buffer::IsRegionUploading(u64 offset, u64 size) const noexcept { - return uploads.IsUsed(offset, size, scheduler->GetMasterSemaphore().KnownGpuTick()); -} - void Buffer::MarkUsage(u64 offset, u64 size) noexcept { - const u64 tick = scheduler->CurrentTick(); - if (tick == usage_tick && offset >= usage_begin && offset + size <= usage_end) { - return; - } - tracker.Track(offset, size, tick, scheduler->GetMasterSemaphore().KnownGpuTick()); - usage_tick = tick; - usage_begin = offset; - usage_end = offset + size; -} - -void Buffer::MarkUpload(std::span copies) noexcept { - last_upload_tick = scheduler->CurrentTick(); - const u64 gpu_tick = scheduler->GetMasterSemaphore().KnownGpuTick(); - for (const VideoCommon::BufferCopy& copy : copies) { - uploads.Track(copy.dst_offset, copy.size, last_upload_tick, gpu_tick); - } -} - -std::span Buffer::CoherentMapping() noexcept { - if (!buffer.IsHostCoherent()) { - return {}; - } - return buffer.Mapped(); + tracker.Track(offset, size); + last_usage_tick = scheduler->CurrentTick(); } VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) { @@ -190,11 +133,6 @@ VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat offset = 0; size = 0; } - const u32 required = - static_cast(device->TexelBufferAlignment(VideoCore::Surface::BytesPerBlock(format))); - const u32 misalign = offset % required; - offset -= misalign; - size += misalign; const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) { return offset == view.offset && size == view.size && format == view.format; })}; @@ -279,20 +217,16 @@ public: scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset, dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) { - const VkBufferCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, + const VkBufferCopy copy{ .srcOffset = src_offset, .dstOffset = 0, .size = size_bytes, }; - const VkBufferMemoryBarrier2 write_barrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + const VkBufferMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT, - .dstAccessMask = VK_ACCESS_2_INDEX_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = dst_buffer, @@ -300,7 +234,8 @@ public: .size = size_bytes, }; cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy); - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier); }); } else { buffer.Flush(); @@ -328,6 +263,7 @@ protected: StagingBufferPool& staging_pool; vk::Buffer buffer{}; + MemoryCommit memory_commit{}; VkIndexType index_type{}; u32 num_indices = 0; }; @@ -421,23 +357,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_}, quad_index_pass(device, scheduler, descriptor_pool, staging_pool, compute_pass_descriptor_queue), - multi_range_buffers(device_), - read_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = device_.GetBufferUserStages(), - .srcAccessMask = vk::ACCESS_BUFFER_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = vk::ACCESS_TRANSFER, - }, - write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = device_.GetBufferConsumerStages(), - .dstAccessMask = device_.GetBufferConsumerAccess(), - } { + multi_range_buffers(device_) { const VkDriverIdKHR driver_id = device.GetDriverID(); limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; @@ -490,32 +410,24 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const { return static_cast(device.GetStorageBufferAlignment()); } -void BufferCacheRuntime::TickFrame(Common::SlotVector&) noexcept { - multi_range_buffers.DrainRetired(scheduler); +void BufferCacheRuntime::TickFrame(Common::SlotVector& slot_buffers) noexcept { + for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { + if (scheduler.IsFree(it->LastUsageTick())) { + it->ResetUsageTracking(); + } + } } u64 BufferCacheRuntime::CurrentTick() { - return scheduler.GetMasterSemaphore().CurrentTick(); + return scheduler.CurrentTick(); } -u64 BufferCacheRuntime::RenderPassSerial() const noexcept { - return scheduler.ActiveRenderPassSerial(); +bool BufferCacheRuntime::IsFree(u64 tick) { + return scheduler.IsFree(tick); } -u64 BufferCacheRuntime::WaitForIdleSerial() const noexcept { - return scheduler.WaitForIdleSerial(); -} - -void BufferCacheRuntime::MarkRenderPassWrites() noexcept { - scheduler.MarkRenderPassWrites(); -} - -u64 BufferCacheRuntime::KnownGpuTick() { - return scheduler.GetMasterSemaphore().KnownGpuTick(); -} - -void BufferCacheRuntime::Wait(u64 buffer_tick) { - scheduler.Wait(buffer_tick); +void BufferCacheRuntime::Wait(u64 tick) { + scheduler.Wait(tick); } void BufferCacheRuntime::Finish() { @@ -534,75 +446,75 @@ bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer, return can_use_upload_cmdbuf; } -std::span BufferCacheRuntime::DirectDownloadSpan(Buffer& buffer) { - const std::span mapping = buffer.CoherentMapping(); - if (mapping.empty()) { - return mapping; - } - if ((std::max)(buffer.getWriteTick(), buffer.LastUploadTick()) > host_read_tick) { - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record( - [](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(HOST_READ_BARRIER); }); - host_read_tick = scheduler.CurrentTick(); - } - scheduler.Wait(host_read_tick); - return mapping; -} - -std::span BufferCacheRuntime::DirectUploadSpan( - Buffer& buffer, std::span copies) { - const std::span mapping = buffer.CoherentMapping(); - const bool uploading = - std::ranges::any_of(copies, [&buffer](const VideoCommon::BufferCopy& copy) { - return buffer.IsRegionUploading(copy.dst_offset, copy.size); - }); - if (mapping.empty() || uploading || !CanReorderUpload(buffer, copies)) { - return {}; - } - return mapping; -} - void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, std::span copies, bool barrier, bool can_reorder_upload) { if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { return; } + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }; + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; // Measuring a popular game, this number never exceeds the specified size once data is warmed up - boost::container::small_vector vk_copies(copies.size()); + boost::container::small_vector vk_copies(copies.size()); std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) { scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies]( vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { - upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, VideoCommon::FixSmallVectorADL(vk_copies)); + upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies); }); return; } scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { + scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); } - cmdbuf.CopyBuffer(src_buffer, dst_buffer, VideoCommon::FixSmallVectorADL(vk_copies)); + cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies); if (barrier) { - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, WRITE_BARRIER); } }); } void BufferCacheRuntime::PreCopyBarrier() { + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(read_barrier); + scheduler.Record([](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, READ_BARRIER); }); } void BufferCacheRuntime::PostCopyBarrier() { + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(write_barrier); + scheduler.Record([](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, WRITE_BARRIER); }); } @@ -610,12 +522,26 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si if (dest_buffer == VK_NULL_HANDLE) { return; } + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }; + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(read_barrier); + scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, READ_BARRIER); cmdbuf.FillBuffer(dest_buffer, offset, size, value); - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, WRITE_BARRIER); }); } @@ -652,40 +578,29 @@ bool BufferCacheRuntime::BindMultiRangeStorageBuffer(u64 key, bool is_written) { void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 base_vertex, u32 num_indices, VkBuffer buffer, - u32 offset, u32 size) { + u32 offset, [[maybe_unused]] u32 size) { VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format); VkDeviceSize vk_offset = offset; - VkDeviceSize vk_size = size; VkBuffer vk_buffer = buffer; if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) { vk_index_type = VK_INDEX_TYPE_UINT32; - vk_size = VK_WHOLE_SIZE; std::tie(vk_buffer, vk_offset) = quad_index_pass.Assemble(index_format, num_indices, base_vertex, buffer, offset, topology == PrimitiveTopology::QuadStrip); } else if (vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported()) { vk_index_type = VK_INDEX_TYPE_UINT16; if (uint8_pass) { - vk_size = VK_WHOLE_SIZE; std::tie(vk_buffer, vk_offset) = uint8_pass->Assemble(num_indices, buffer, offset); } else if (device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) { ReserveNullBuffer(); vk_buffer = *null_buffer; vk_offset = 0; - vk_size = VK_WHOLE_SIZE; } } if (vk_buffer == VK_NULL_HANDLE) { // Vulkan doesn't support null index buffers. Replace it with our own null buffer. ReserveNullBuffer(); vk_buffer = *null_buffer; - vk_size = VK_WHOLE_SIZE; - } - if (device.IsKhrMaintenance5Supported()) { - scheduler.Record([vk_buffer, vk_offset, vk_size, vk_index_type](vk::CommandBuffer cmdbuf) { - cmdbuf.BindIndexBuffer2KHR(vk_buffer, vk_offset, vk_size, vk_index_type); - }); - return; } scheduler.Record([vk_buffer, vk_offset, vk_index_type](vk::CommandBuffer cmdbuf) { cmdbuf.BindIndexBuffer(vk_buffer, vk_offset, vk_index_type); @@ -710,73 +625,16 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir } } -bool BufferCacheRuntime::BindQuadIndices(PrimitiveTopology topology, IndexFormat index_format, - u32 base_vertex, u32 num_indices, - std::span indices) { - const size_t index_size = size_t{1} << static_cast(index_format); - if (indices.size() < num_indices * index_size) { - return false; - } - u32 quads = num_indices / 4; - u32 stride = 4; - std::array swizzle{0, 1, 2, 0, 2, 3}; - if (topology == PrimitiveTopology::QuadStrip) { - quads = 0; - if (num_indices >= 2) { - quads = (num_indices - 2) / 2; - } - stride = 2; - swizzle = {0, 3, 1, 0, 2, 3}; - } - const size_t size = (std::max)(quads, 1U) * sizeof(swizzle); - const StagingBufferRef staging = staging_pool.Request(size, MemoryUsage::Upload); - const auto convert = [&](auto index_type) { - u8* output = staging.mapped_span.data(); - for (u32 quad = 0; quad < quads; ++quad) { - std::array triangles; - for (size_t vertex = 0; vertex < triangles.size(); ++vertex) { - decltype(index_type) index; - std::memcpy(&index, - indices.data() + (quad * stride + swizzle[vertex]) * sizeof(index), - sizeof(index)); - triangles[vertex] = index + base_vertex; - } - std::memcpy(output, triangles.data(), sizeof(triangles)); - output += sizeof(triangles); - } - }; - switch (index_format) { - case IndexFormat::UnsignedByte: - convert(u8{}); - break; - case IndexFormat::UnsignedShort: - convert(u16{}); - break; - case IndexFormat::UnsignedInt: - convert(u32{}); - break; - } - BindIndexBuffer(PrimitiveTopology::Triangles, IndexFormat::UnsignedInt, 0, 0, staging.buffer, - static_cast(staging.offset), static_cast(size)); - return true; -} - void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) { if (index >= device.GetMaxVertexInputBindings()) { return; } if (device.IsExtExtendedDynamicStateSupported()) { - const bool dynamic_stride = !device.IsExtVertexInputDynamicStateSupported(); - scheduler.Record([index, buffer, offset, size, stride, - dynamic_stride](vk::CommandBuffer cmdbuf) { + scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; const VkDeviceSize vk_stride = stride; - const VkDeviceSize* strides = nullptr; - if (dynamic_stride) { - strides = &vk_stride; - } - cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, strides); + cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); }); } else { if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { @@ -790,56 +648,36 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset } } -template -void BufferCacheRuntime::RecordVertexBuffers(const VideoCommon::HostBindings& bindings, - u32 count) { - VertexBindings vertex{}; - vertex.first = bindings.min_index; - vertex.count = count; - for (u32 i = 0; i < count; ++i) { - vertex.buffers[i] = bindings.buffers[i]->Handle(); - vertex.offsets[i] = bindings.offsets[i]; - vertex.sizes[i] = bindings.sizes[i]; - vertex.strides[i] = bindings.strides[i]; - if (vertex.buffers[i] == VK_NULL_HANDLE) { - vertex.offsets[i] = 0; - vertex.sizes[i] = VK_WHOLE_SIZE; +void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { + boost::container::static_vector buffer_handles(bindings.buffers.size()); + for (u32 i = 0; i < bindings.buffers.size(); ++i) { + auto handle = bindings.buffers[i]->Handle(); + if (handle == VK_NULL_HANDLE) { + bindings.offsets[i] = 0; + bindings.sizes[i] = VK_WHOLE_SIZE; if (!device.HasNullDescriptor()) { ReserveNullBuffer(); - vertex.buffers[i] = *null_buffer; + handle = *null_buffer; } } + buffer_handles[i] = handle; + } + const u32 device_max = device.GetMaxVertexInputBindings(); + const u32 min_binding = (std::min)(bindings.min_index, device_max); + const u32 max_binding = (std::min)(bindings.max_index, device_max); + const u32 binding_count = max_binding - min_binding; + if (binding_count == 0) { + return; } if (device.IsExtExtendedDynamicStateSupported()) { - const bool dynamic_stride = !device.IsExtVertexInputDynamicStateSupported(); - scheduler.Record([vertex, dynamic_stride](vk::CommandBuffer cmdbuf) { - const VkDeviceSize* strides = nullptr; - if (dynamic_stride) { - strides = vertex.strides.data(); - } - cmdbuf.BindVertexBuffers2EXT(vertex.first, vertex.count, vertex.buffers.data(), - vertex.offsets.data(), vertex.sizes.data(), strides); + scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data()); + }); + } else { + scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data()); }); - return; } - scheduler.Record([vertex](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers(vertex.first, vertex.count, vertex.buffers.data(), - vertex.offsets.data()); - }); -} - -void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { - const u32 device_max = device.GetMaxVertexInputBindings(); - const u32 count = (std::min)(bindings.max_index, device_max) - - (std::min)(bindings.min_index, device_max); - if (count == 0) { - return; - } - if (count <= COMPACT_VERTEX_BINDINGS) { - RecordVertexBuffers(bindings, count); - return; - } - RecordVertexBuffers(bindings, count); } void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 3209019bdf..fff1f8164d 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -55,19 +55,19 @@ public: return buffer.Location(); } - [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept; - - [[nodiscard]] bool IsRegionUploading(u64 offset, u64 size) const noexcept; + [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept { + return tracker.IsUsed(offset, size); + } void MarkUsage(u64 offset, u64 size) noexcept; - void MarkUpload(std::span copies) noexcept; - - [[nodiscard]] u64 LastUploadTick() const noexcept { - return last_upload_tick; + void ResetUsageTracking() noexcept { + tracker.Reset(); } - [[nodiscard]] std::span CoherentMapping() noexcept; + [[nodiscard]] u64 LastUsageTick() const noexcept { + return last_usage_tick; + } operator VkBuffer() const noexcept { return *buffer; @@ -86,12 +86,8 @@ private: vk::Buffer buffer; std::vector views; VideoCommon::UsageTracker tracker; - VideoCommon::UsageTracker uploads; VkDeviceAddress device_address{}; - u64 usage_tick{}; - u64 usage_begin{}; - u64 usage_end{}; - u64 last_upload_tick{}; + u64 last_usage_tick{}; bool is_null{}; bool sparse_compatible{}; }; @@ -112,19 +108,13 @@ public: ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool); - void TickFrame(Common::SlotVector&) noexcept; + void TickFrame(Common::SlotVector& slot_buffers) noexcept; u64 CurrentTick(); - u64 RenderPassSerial() const noexcept; + bool IsFree(u64 tick); - u64 WaitForIdleSerial() const noexcept; - - void MarkRenderPassWrites() noexcept; - - u64 KnownGpuTick(); - - void Wait(u64 buffer_tick); + void Wait(u64 tick); void Finish(); @@ -144,11 +134,6 @@ public: bool CanReorderUpload(const Buffer& buffer, std::span copies); - [[nodiscard]] std::span DirectDownloadSpan(Buffer& buffer); - - [[nodiscard]] std::span DirectUploadSpan(Buffer& buffer, - std::span copies); - void FreeDeferredStagingBuffer(StagingBufferRef& ref); void PreCopyBarrier(); @@ -157,13 +142,6 @@ public: std::span copies, bool barrier, bool can_reorder_upload = false); - void CopyBuffer(Buffer& dst_buffer, VkBuffer src_buffer, - std::span copies, bool barrier, - bool can_reorder_upload = false) { - dst_buffer.MarkUpload(copies); - CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier, can_reorder_upload); - } - void PostCopyBarrier(); void ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value); @@ -173,9 +151,6 @@ public: void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); - bool BindQuadIndices(PrimitiveTopology topology, IndexFormat index_format, u32 base_vertex, - u32 num_indices, std::span indices); - void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); void BindVertexBuffers(VideoCommon::HostBindings& bindings); @@ -217,7 +192,7 @@ public: .memory_offset = location.offset, .offset = offset, .size = size, - .content_serial = buffer.ContentSerial(), + .write_tick = buffer.getWriteTick(), .memory_type = location.memory_type, }); multi_range_total += size; @@ -225,6 +200,10 @@ public: bool BindMultiRangeStorageBuffer(u64 key, bool is_written); + void InvalidateMultiRange(u64 key) { + multi_range_buffers.Invalidate(key); + } + void OnBufferDeleted(const Buffer& buffer) { multi_range_buffers.DropOwner(scheduler, buffer.Handle()); } @@ -265,9 +244,6 @@ private: VkFormat TexelBufferFormat(VideoCore::Surface::PixelFormat format) const; - template - void RecordVertexBuffers(const VideoCommon::HostBindings& bindings, u32 count); - void ReserveNullBuffer(); vk::Buffer CreateNullBuffer(); @@ -289,10 +265,6 @@ private: boost::container::small_vector multi_range_sources; VkDeviceSize multi_range_total{}; - VkMemoryBarrier2 read_barrier; - VkMemoryBarrier2 write_barrier; - u64 host_read_tick = 0; - bool limit_dynamic_storage_buffers = false; u32 max_dynamic_storage_buffers = (std::numeric_limits::max)(); }; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index f1b0248233..ab58560170 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -12,22 +12,17 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" -#include - #include "common/assert.h" #include "common/common_types.h" #include "common/div_ceil.h" #include "common/vector_math.h" #include "video_core/host_shaders/astc_decoder_comp_spv.h" -#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h" -#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h" -#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h" #include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h" #include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h" #include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" -#include "video_core/host_shaders/vulkan_indirect_quads_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -190,26 +185,6 @@ struct AstcPushConstants { u32 block_height_mask; }; -struct BlockLinear3DImagePushConstants { - alignas(16) std::array origin; - alignas(16) std::array destination; - u32 bytes_per_block_log2; - u32 slice_size; - u32 block_size; - u32 x_shift; - u32 block_height; - u32 block_height_mask; - u32 block_depth; - u32 block_depth_mask; -}; - -struct PitchUnswizzlePushConstants { - std::array origin; - std::array destination; - u32 bytes_per_block_log2; - u32 pitch; -}; - struct QueriesPrefixScanPushConstants { u32 min_accumulation_base; u32 max_accumulation_base; @@ -293,7 +268,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor .layout = *layout, .basePipelineHandle = {}, .basePipelineIndex = 0, - }); + }, device.StaticPipelineCache()); } ComputePass::~ComputePass() = default; @@ -322,20 +297,19 @@ std::pair Uint8Pass::Assemble(u32 num_vertices, VkBuffer scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, descriptor_data, num_vertices](vk::CommandBuffer cmdbuf) { static constexpr u32 DISPATCH_SIZE = 1024; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT, - .dstAccessMask = VK_ACCESS_2_INDEX_READ_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, }; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.Dispatch(Common::DivCeil(num_vertices, DISPATCH_SIZE), 1, 1); - cmdbuf.PipelineBarrier(WRITE_BARRIER); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, WRITE_BARRIER); }); return {staging.buffer, staging.offset}; } @@ -368,17 +342,7 @@ std::pair QuadIndexedPass::Assemble( return 2; }(); const u32 input_size = num_vertices << index_shift; - u32 quads = num_vertices / 4; - if (is_strip) { - quads = 0; - if (num_vertices >= 2) { - quads = (num_vertices - 2) / 2; - } - } - if (quads == 0) { - quads = 1; - } - const u32 num_tri_vertices = quads * 6; + const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6; const std::size_t staging_size = num_tri_vertices * sizeof(u32); const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); @@ -392,13 +356,11 @@ std::pair QuadIndexedPass::Assemble( scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, index_shift, is_strip](vk::CommandBuffer cmdbuf) { static constexpr u32 DISPATCH_SIZE = 1024; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT, - .dstAccessMask = VK_ACCESS_2_INDEX_READ_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, }; const std::array push_constants{base_vertex, index_shift, is_strip ? 1u : 0u}; const VkDescriptorSet set = descriptor_allocator.Commit(); @@ -408,58 +370,8 @@ std::pair QuadIndexedPass::Assemble( cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants), &push_constants); cmdbuf.Dispatch(Common::DivCeil(num_tri_vertices, DISPATCH_SIZE), 1, 1); - cmdbuf.PipelineBarrier(WRITE_BARRIER); - }); - return {staging.buffer, staging.offset}; -} - -IndirectQuadsPass::IndirectQuadsPass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, scheduler_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, - INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE, VULKAN_INDIRECT_QUADS_COMP_SPV), - scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, - compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} - -IndirectQuadsPass::~IndirectQuadsPass() = default; - -std::pair IndirectQuadsPass::Assemble(u32 num_draws, u32 stride, - VkBuffer src_buffer, u32 src_offset) { - u32 src_stride = stride / static_cast(sizeof(u32)); - if (src_stride < COMMAND_WORDS) { - src_stride = COMMAND_WORDS; - } - const u32 input_size = num_draws * src_stride * static_cast(sizeof(u32)); - const std::size_t staging_size = std::size_t(num_draws) * COMMAND_WORDS * sizeof(u32); - const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); - - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); - compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size); - const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; - - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, descriptor_data, num_draws, src_stride](vk::CommandBuffer cmdbuf) { - static constexpr u32 DISPATCH_SIZE = 32; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, - .dstAccessMask = VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT, - }; - const std::array push_constants{num_draws, src_stride}; - const VkDescriptorSet set = descriptor_allocator.Commit(); - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants), - &push_constants); - cmdbuf.Dispatch(Common::DivCeil(num_draws, DISPATCH_SIZE), 1, 1); - cmdbuf.PipelineBarrier(WRITE_BARRIER); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, WRITE_BARRIER); }); return {staging.buffer, staging.offset}; } @@ -487,21 +399,17 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) { - const VkMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = device.GetBufferUserStages(), - .srcAccessMask = vk::ACCESS_BUFFER_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = vk::ACCESS_SHADER_RESOURCES, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, }; - static constexpr VkMemoryBarrier2 write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT, - .dstAccessMask = VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, }; const ConditionalRenderingResolvePushConstants uniforms{ .compare_to_zero = compare_to_zero ? 1U : 0U, @@ -509,12 +417,15 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, + write_barrier); }); } @@ -536,22 +447,14 @@ QueriesPrefixScanPass::QueriesPrefixScanPass( void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffer, VkBuffer src_buffer, size_t number_of_sums, size_t min_accumulation_limit, size_t max_accumulation_limit) { - const VkMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = device.GetBufferUserStages(), - .srcAccessMask = vk::ACCESS_BUFFER_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = vk::ACCESS_SHADER_RESOURCES, - }; - const VkMemoryBarrier2 write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = device.GetBufferConsumerStages(), - .dstAccessMask = device.GetBufferConsumerAccess(), - }; + constexpr VkAccessFlags BASE_DST_ACCESS = VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_INDEX_READ_BIT | + VK_ACCESS_UNIFORM_READ_BIT; + const VkAccessFlags conditional_access = + device.IsExtConditionalRendering() ? VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT : 0; size_t current_runs = number_of_sums; size_t offset = 0; while (current_runs != 0) { @@ -568,8 +471,19 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, descriptor_data, min_accumulation_limit, max_accumulation_limit, - runs_to_do, used_offset, read_barrier, - write_barrier](vk::CommandBuffer cmdbuf) { + runs_to_do, used_offset, conditional_access](vk::CommandBuffer cmdbuf) { + static constexpr VkMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + }; + const VkMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = BASE_DST_ACCESS | conditional_access, + }; const QueriesPrefixScanPushConstants uniforms{ .min_accumulation_base = static_cast(min_accumulation_limit), .max_accumulation_base = static_cast(max_accumulation_limit), @@ -579,97 +493,18 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, write_barrier); }); } } -namespace { - -template -void RecordUnswizzle(Scheduler& scheduler, bool reorder, Func&& func) { - if (reorder) { - scheduler.RecordWithUploadBuffer( - [func = std::forward(func)](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { - func(upload_cmdbuf); - }); - return; - } - scheduler.Record(std::forward(func)); -} - -void RecordUnswizzleBeginBarrier(Scheduler& scheduler, bool reorder, VkPipeline vk_pipeline, - VkImage vk_image, VkImageAspectFlags aspect_mask, - bool is_initialized) { - VkAccessFlags2 src_access = VK_ACCESS_2_NONE; - VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkPipelineStageFlags2 src_stage = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; - if (is_initialized) { - src_access = vk::ACCESS_IMAGE_WRITES; - old_layout = VK_IMAGE_LAYOUT_GENERAL; - src_stage = vk::PIPELINE_STAGE_IMAGE_USERS; - } - RecordUnswizzle(scheduler, reorder, [vk_pipeline, vk_image, aspect_mask, src_access, - old_layout, src_stage](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 image_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = src_stage, - .srcAccessMask = src_access, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT, - .oldLayout = old_layout, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = vk_image, - .subresourceRange{ - .aspectMask = aspect_mask, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - cmdbuf.PipelineBarrier(image_barrier); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); - }); -} - -void RecordUnswizzleEndBarrier(Scheduler& scheduler, bool reorder, VkImage vk_image, - VkImageAspectFlags aspect_mask) { - RecordUnswizzle(scheduler, reorder, [vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 image_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = vk_image, - .subresourceRange{ - .aspectMask = aspect_mask, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - cmdbuf.PipelineBarrier(image_barrier); - }); -} - -} // Anonymous namespace - ASTCDecoderPass::ASTCDecoderPass(const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, @@ -685,19 +520,43 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, Scheduler& scheduler_, ASTCDecoderPass::~ASTCDecoderPass() = default; void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, - std::span swizzles, - bool reorder) { + std::span swizzles) { using namespace VideoCommon::Accelerated; const std::array block_dims{ VideoCore::Surface::DefaultBlockWidth(image.info.format), VideoCore::Surface::DefaultBlockHeight(image.info.format), }; - if (!reorder) { - scheduler.RequestOutsideRenderPassOperationContext(); - } + scheduler.RequestOutsideRenderPassOperationContext(); + const VkPipeline vk_pipeline = *pipeline; const VkImageAspectFlags aspect_mask = image.AspectMask(); const VkImage vk_image = image.Handle(); - RecordUnswizzleBeginBarrier(scheduler, reorder, *pipeline, vk_image, aspect_mask, !reorder); + const bool is_initialized = image.ExchangeInitialization(); + scheduler.Record([vk_pipeline, vk_image, aspect_mask, + is_initialized](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT + : VK_ACCESS_NONE), + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER + : VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); + }); for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { const size_t input_offset = swizzle.buffer_offset + map.offset; const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U); @@ -715,9 +574,8 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, ASSERT(params.origin == (std::array{0, 0, 0})); ASSERT(params.destination == (std::array{0, 0, 0})); ASSERT(params.bytes_per_block_log2 == 4); - RecordUnswizzle(scheduler, reorder, - [this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims, - params, descriptor_data](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims, + params, descriptor_data](vk::CommandBuffer cmdbuf) { const AstcPushConstants uniforms{ .blocks_dims = block_dims, .layer_stride = params.layer_stride, @@ -733,172 +591,285 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z); }); } - RecordUnswizzleEndBarrier(scheduler, reorder, vk_image, aspect_mask); + scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier); + }); + scheduler.Finish(); } -BlockLinearUnswizzleImage2DPass::BlockLinearUnswizzleImage2DPass( - const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, +constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0; +constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1; + +struct alignas(16) BlockLinearUnswizzle3DPushConstants { + u32 blocks_dim[3]; // Offset 0 + u32 bytes_per_block_log2; // Offset 12 + + u32 origin[3]; // Offset 16 + u32 slice_size; // Offset 28 + + u32 block_size; // Offset 32 + u32 x_shift; // Offset 36 + u32 block_height; // Offset 40 + u32 block_height_mask; // Offset 44 + + u32 block_depth; // Offset 48 + u32 block_depth_mask; // Offset 52 + s32 _pad; // Offset 56 + + s32 destination[3]; // Offset 60 + s32 _pad_end; // Offset 72 +}; +static_assert(sizeof(BlockLinearUnswizzle3DPushConstants) <= 128); + +BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass( + const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, - ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE< - sizeof(VideoCommon::Accelerated::BlockLinearSwizzle2DParams)>, - BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV), - scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, + : ComputePass(device_, scheduler_, descriptor_pool_, + std::array{{ + { + .binding = BL3D_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = BL3D_BINDING_OUTPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + }}, + std::array{{ + { + .dstBinding = BL3D_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = BL3D_BINDING_OUTPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + } + }}, + DescriptorBankInfo{ + .uniform_buffers = 0, + .storage_buffers = 2, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 0, + .score = 2, + }, + COMPUTE_PUSH_CONSTANT_RANGE, + BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV), + scheduler{scheduler_}, + staging_buffer_pool{staging_buffer_pool_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} -BlockLinearUnswizzleImage2DPass::~BlockLinearUnswizzleImage2DPass() = default; +BlockLinearUnswizzle3DPass::~BlockLinearUnswizzle3DPass() = default; -void BlockLinearUnswizzleImage2DPass::Unswizzle( - Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder) { +// God have mercy on my soul +void BlockLinearUnswizzle3DPass::Unswizzle( + Image& image, + const StagingBufferRef& swizzled, + std::span swizzles, + u32 z_start, u32 z_count) +{ using namespace VideoCommon::Accelerated; - if (!reorder) { - scheduler.RequestOutsideRenderPassOperationContext(); - } - const VkImageAspectFlags aspect_mask = image.AspectMask(); - const VkImage vk_image = image.Handle(); - RecordUnswizzleBeginBarrier(scheduler, reorder, *pipeline, vk_image, aspect_mask, !reorder); - for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { - const size_t input_offset = swizzle.buffer_offset + map.offset; - const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); - const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U); - const u32 num_dispatches_z = image.info.resources.layers; - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, - image.guest_size_bytes - swizzle.buffer_offset); - compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); - const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; + const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth); - const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); - RecordUnswizzle(scheduler, reorder, - [this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params, - descriptor_data](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet set = descriptor_allocator.Commit(); - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); - cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z); - }); + // Allocate or grow to cover this batch's slice count + image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES); + + ASSERT(swizzles.size() == 1); + const auto& sw = swizzles[0]; + const auto params = MakeBlockLinearSwizzle3DParams(sw, image.info); + + const u32 blocks_x = (image.info.size.width + 3) / 4; + const u32 blocks_y = (image.info.size.height + 3) / 4; + + scheduler.RequestOutsideRenderPassOperationContext(); + for (u32 z_offset = 0; z_offset < z_count; z_offset += MAX_BATCH_SLICES) { + const u32 current_chunk_slices = (std::min)(MAX_BATCH_SLICES, z_count - z_offset); + const u32 current_z_start = z_start + z_offset; + + UnswizzleChunk(image, swizzled, sw, params, blocks_x, blocks_y, + current_z_start, current_chunk_slices); } - RecordUnswizzleEndBarrier(scheduler, reorder, vk_image, aspect_mask); } -BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass( - const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, - ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE, - BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV), - scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, - compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} +void BlockLinearUnswizzle3DPass::UnswizzleChunk( + Image& image, + const StagingBufferRef& swizzled, + const VideoCommon::SwizzleParameters& sw, + const BlockLinearSwizzle3DParams& params, + u32 blocks_x, u32 blocks_y, + u32 z_start, u32 z_count) +{ + BlockLinearUnswizzle3DPushConstants pc{}; + pc.origin[0] = params.origin[0]; + pc.origin[1] = params.origin[1]; + pc.origin[2] = z_start; // Current chunk's Z start -BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default; + pc.destination[0] = params.destination[0]; + pc.destination[1] = params.destination[1]; + pc.destination[2] = 0; // Shader writes to start of output buffer -void BlockLinearUnswizzleImage3DPass::Unswizzle( - Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder) { - using namespace VideoCommon::Accelerated; - if (!reorder) { - scheduler.RequestOutsideRenderPassOperationContext(); - } - const VkImageAspectFlags aspect_mask = image.AspectMask(); - const VkImage vk_image = image.Handle(); - RecordUnswizzleBeginBarrier(scheduler, reorder, *pipeline, vk_image, aspect_mask, !reorder); - for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { - const size_t input_offset = swizzle.buffer_offset + map.offset; - const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U); - const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U); - const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 2U); + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.slice_size = params.slice_size; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + pc.block_depth = params.block_depth; + pc.block_depth_mask = params.block_depth_mask; - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, - image.guest_size_bytes - swizzle.buffer_offset); - compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); - const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; + pc.blocks_dim[0] = blocks_x; + pc.blocks_dim[1] = blocks_y; + pc.blocks_dim[2] = z_count; // Only process the count - const auto p = MakeBlockLinearSwizzle3DParams(swizzle, image.info); - const BlockLinear3DImagePushConstants params{ - .origin = p.origin, - .destination = p.destination, - .bytes_per_block_log2 = p.bytes_per_block_log2, - .slice_size = p.slice_size, - .block_size = p.block_size, - .x_shift = p.x_shift, - .block_height = p.block_height, - .block_height_mask = p.block_height_mask, - .block_depth = p.block_depth, - .block_depth_mask = p.block_depth_mask, + compute_pass_descriptor_queue.Acquire(scheduler, 3); + compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, + sw.buffer_offset + swizzled.offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0, + image.compute_unswizzle_buffer_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(blocks_x, 8u); + const u32 gy = Common::DivCeil(blocks_y, 8u); + const u32 gz = Common::DivCeil(z_count, 4u); + + const u32 bytes_per_block = 1u << pc.bytes_per_block_log2; + const VkDeviceSize output_slice_size = + static_cast(blocks_x) * blocks_y * bytes_per_block; + const VkDeviceSize barrier_size = output_slice_size * z_count; + + const bool is_first_chunk = (z_start == 0); + + const VkBuffer out_buffer = *image.compute_unswizzle_buffer; + const VkImage dst_image = image.Handle(); + const VkImageAspectFlags aspect = image.AspectMask(); + const u32 image_width = image.info.size.width; + const u32 image_height = image.info.size.height; + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, z_start, z_count, + barrier_size, is_first_chunk, out_buffer, dst_image, aspect, + image_width, image_height + ](vk::CommandBuffer cmdbuf) { + + if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { + return; + } + + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, gz); + + // Single barrier for compute -> transfer (buffer ready, image transition) + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = 0, + .size = barrier_size, }; - RecordUnswizzle(scheduler, reorder, - [this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params, - descriptor_data](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet set = descriptor_allocator.Commit(); - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); - cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z); - }); - } - RecordUnswizzleEndBarrier(scheduler, reorder, vk_image, aspect_mask); -} -PitchUnswizzlePass::PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_) - : ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS, - ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, - COMPUTE_PUSH_CONSTANT_RANGE, - PITCH_UNSWIZZLE_COMP_SPV), - scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, - compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} - -PitchUnswizzlePass::~PitchUnswizzlePass() = default; - -void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map, - std::span swizzles, - bool reorder) { - const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format); - ASSERT(std::has_single_bit(bytes_per_block)); - if (!reorder) { - scheduler.RequestOutsideRenderPassOperationContext(); - } - const VkImageAspectFlags aspect_mask = image.AspectMask(); - const VkImage vk_image = image.Handle(); - RecordUnswizzleBeginBarrier(scheduler, reorder, *pipeline, vk_image, aspect_mask, !reorder); - for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { - const size_t input_offset = swizzle.buffer_offset + map.offset; - const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); - const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U); - - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, - image.guest_size_bytes - swizzle.buffer_offset); - compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); - const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; - - const PitchUnswizzlePushConstants params{ - .origin = {0, 0}, - .destination = {0, 0}, - .bytes_per_block_log2 = static_cast(std::countr_zero(bytes_per_block)), - .pitch = image.info.pitch, + // Image layout transition + const VkImageMemoryBarrier pre_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = is_first_chunk ? VkAccessFlags{} : + static_cast(VK_ACCESS_TRANSFER_WRITE_BIT), + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = is_first_chunk ? VK_IMAGE_LAYOUT_UNDEFINED : + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange = {aspect, 0, 1, 0, 1}, }; - RecordUnswizzle(scheduler, reorder, - [this, num_dispatches_x, num_dispatches_y, params, - descriptor_data](vk::CommandBuffer cmdbuf) { - const VkDescriptorSet set = descriptor_allocator.Commit(); - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); - cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1); - }); - } - RecordUnswizzleEndBarrier(scheduler, reorder, vk_image, aspect_mask); + + // Single barrier handles both buffer and image + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + nullptr, buffer_barrier, pre_barrier + ); + + // Copy chunk to correct Z position in image + const VkBufferImageCopy copy{ + .bufferOffset = 0, // Read from start of staging buffer + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = {aspect, 0, 0, 1}, + .imageOffset = {0, 0, static_cast(z_start)}, // Write to correct Z + .imageExtent = {image_width, image_height, z_count}, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + + // Post-copy transition + const VkImageMemoryBarrier post_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange = {aspect, 0, 1, 0, 1}, + }; + + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + nullptr, nullptr, post_barrier + ); + }); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 7517b65aee..3d30aa6bdc 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -17,6 +17,7 @@ #include "video_core/texture_cache/types.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" +#include "video_core/texture_cache/accelerated_swizzle.h" namespace VideoCommon { struct SwizzleParameters; @@ -24,6 +25,8 @@ struct SwizzleParameters; namespace Vulkan { +using VideoCommon::Accelerated::BlockLinearSwizzle3DParams; + class Device; class StagingBufferPool; class Scheduler; @@ -88,25 +91,6 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; -class IndirectQuadsPass final : public ComputePass { -public: - explicit IndirectQuadsPass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_); - ~IndirectQuadsPass(); - - static constexpr u32 COMMAND_WORDS = 5; - - std::pair Assemble(u32 num_draws, u32 stride, VkBuffer src_buffer, - u32 src_offset); - -private: - Scheduler& scheduler; - StagingBufferPool& staging_buffer_pool; - ComputePassDescriptorQueue& compute_pass_descriptor_queue; -}; - class ConditionalRenderingResolvePass final : public ComputePass { public: explicit ConditionalRenderingResolvePass( @@ -144,7 +128,7 @@ public: ~ASTCDecoderPass(); void Assemble(Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder); + std::span swizzles); private: Scheduler& scheduler; @@ -153,50 +137,26 @@ private: MemoryAllocator& memory_allocator; }; -class PitchUnswizzlePass final : public ComputePass { +class BlockLinearUnswizzle3DPass final : public ComputePass { public: - explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, - DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_); - ~PitchUnswizzlePass(); + explicit BlockLinearUnswizzle3DPass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + StagingBufferPool& staging_buffer_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle3DPass(); - void Unswizzle(Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder); + void Unswizzle(Image& image, + const StagingBufferRef& swizzled, + std::span swizzles, + u32 z_start, u32 z_count); -private: - Scheduler& scheduler; - StagingBufferPool& staging_buffer_pool; - ComputePassDescriptorQueue& compute_pass_descriptor_queue; -}; - -class BlockLinearUnswizzleImage3DPass final : public ComputePass { -public: - explicit BlockLinearUnswizzleImage3DPass( - const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_); - ~BlockLinearUnswizzleImage3DPass(); - - void Unswizzle(Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder); - -private: - Scheduler& scheduler; - StagingBufferPool& staging_buffer_pool; - ComputePassDescriptorQueue& compute_pass_descriptor_queue; -}; - -class BlockLinearUnswizzleImage2DPass final : public ComputePass { -public: - explicit BlockLinearUnswizzleImage2DPass( - const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, - StagingBufferPool& staging_buffer_pool_, - ComputePassDescriptorQueue& compute_pass_descriptor_queue_); - ~BlockLinearUnswizzleImage2DPass(); - - void Unswizzle(Image& image, const StagingBufferRef& map, - std::span swizzles, bool reorder); + void UnswizzleChunk( + Image& image, + const StagingBufferRef& swizzled, + const VideoCommon::SwizzleParameters& sw, + const BlockLinearSwizzle3DParams& params, + u32 blocks_x, u32 blocks_y, + u32 z_start, u32 z_count); private: Scheduler& scheduler; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 840ff5897b..29356cfcec 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -247,6 +247,10 @@ bool ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, buffer_cache.UpdateComputeBuffers(); buffer_cache.BindHostComputeBuffers(); + if (buffer_cache.any_buffer_uploaded) { + buffer_cache.runtime.PostCopyBarrier(); + buffer_cache.any_buffer_uploaded = false; + } RescalingPushConstant rescaling; const VideoCommon::SamplerId* samplers_it{samplers.data()}; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h index 4cde5abcd4..6f96bcc326 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h @@ -17,9 +17,9 @@ class Device; class Scheduler; class DescriptorBufferRing final { - static constexpr size_t FRAMES_IN_FLIGHT = 3; - static constexpr VkDeviceSize TILER_FRAME_SIZE = 5 * 1024 * 1024; - static constexpr VkDeviceSize DESKTOP_FRAME_SIZE = 8 * 1024 * 1024; + static constexpr size_t FRAMES_IN_FLIGHT = 8; + static constexpr VkDeviceSize TILER_FRAME_SIZE = 2 * 1024 * 1024; + static constexpr VkDeviceSize DESKTOP_FRAME_SIZE = 4 * 1024 * 1024; public: explicit DescriptorBufferRing(const Device& device_, MemoryAllocator& memory_allocator); diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 2f4af7c330..b020d99b93 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -125,7 +125,7 @@ vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) { throw vk::Exception(VK_ERROR_OUT_OF_POOL_MEMORY); } -DescriptorPool::DescriptorPool() = default; +DescriptorPool::DescriptorPool(const Device& device_, Scheduler& scheduler) {} DescriptorPool::~DescriptorPool() = default; DescriptorAllocator DescriptorPool::Allocator(const Device& device, Scheduler& scheduler, VkDescriptorSetLayout layout, std::span infos) { diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index a524c2a043..4efeb60373 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -65,8 +65,7 @@ private: class DescriptorPool { public: - -DescriptorPool(); + explicit DescriptorPool(const Device& device, Scheduler& scheduler); ~DescriptorPool(); DescriptorPool& operator=(const DescriptorPool&) = delete; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d2401e42cd..8b5b0bc9c5 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -125,49 +125,22 @@ PixelFormat DecodeFormat(u8 encoded_format) { return PixelFormatFromRenderTargetFormat(format); } -RenderingFormats PipelineFormats(const FixedPipelineState& state, const Device& device) { - std::array color_formats; - std::ranges::transform(state.color_formats, color_formats.begin(), DecodeFormat); - PixelFormat depth_format = PixelFormat::Invalid; +RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& device) { + RenderPassKey key{}; + std::ranges::transform(state.color_formats, key.color_formats.begin(), DecodeFormat); if (state.depth_enabled != 0) { - depth_format = PixelFormatFromDepthFormat( - static_cast(state.depth_format.Value())); + const auto depth_format{static_cast(state.depth_format.Value())}; + key.depth_format = PixelFormatFromDepthFormat(depth_format); + } else { + key.depth_format = PixelFormat::Invalid; } - return MakeRenderingFormats(device, color_formats, depth_format); -} - -struct AttachmentAccess { - u32 touched; - u32 written; -}; - -AttachmentAccess MakeAttachmentAccess(const Maxwell& regs) { - AttachmentAccess access{}; - if (regs.rasterize_enable == 0) { - return access; - } - for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { - size_t mask_index = index; - if (regs.color_mask_common) { - mask_index = 0; - } - const auto& mask = regs.color_mask[mask_index]; - if (mask.R || mask.G || mask.B || mask.A) { - access.touched |= 1u << index; - access.written |= 1u << index; - } - } - if (regs.depth_test_enable || regs.depth_bounds_enable) { - access.touched |= Scheduler::DEPTH_ATTACHMENT_BIT; - } - if (regs.depth_test_enable && regs.depth_write_enabled) { - access.written |= Scheduler::DEPTH_ATTACHMENT_BIT; - } - if (regs.stencil_enable) { - access.touched |= Scheduler::STENCIL_ATTACHMENT_BIT; - access.written |= Scheduler::STENCIL_ATTACHMENT_BIT; - } - return access; + key.samples = MaxwellToVK::MsaaMode(state.msaa_mode); + const bool has_color = std::ranges::any_of(key.color_formats, [](PixelFormat format) { + return format != PixelFormat::Invalid; + }); + key.resolve_color = + key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler(); + return key; } size_t NumAttachments(const FixedPipelineState& state) { @@ -280,8 +253,8 @@ GraphicsPipeline::GraphicsPipeline( const Device& device_, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_, Common::ThreadWorker* worker_thread, - PipelineStatistics* pipeline_statistics, const GraphicsPipelineCacheKey& key_, - std::array stages, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, pipeline_cache(pipeline_cache_), scheduler{scheduler_}, @@ -306,11 +279,6 @@ GraphicsPipeline::GraphicsPipeline( num_descriptor_entries += NumDescriptorEntries(*info); } fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0]; - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (stage_infos[0].loads.Generic(index)) { - vertex_attribute_mask |= 1u << index; - } - } DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; uses_push_descriptor = builder.CanUsePushDescriptor(); @@ -341,10 +309,11 @@ GraphicsPipeline::GraphicsPipeline( } } - auto func{[this, shader_notify, pipeline_statistics] { + auto func{[this, shader_notify, &render_pass_cache, pipeline_statistics] { + const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state, device))}; Validate(); try { - MakePipeline(PipelineFormats(key.state, device)); + MakePipeline(render_pass); } catch (const vk::Exception& exception) { LOG_CRITICAL(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what()); std::scoped_lock lock{build_mutex}; @@ -579,17 +548,13 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { if constexpr (Spec::enabled_stages[4]) { prepare_stage(4); } - texture_cache.UpdateRenderTargets(false); - const std::span view_span{views.data(), views.size()}; - texture_cache.CheckFeedbackLoop(view_span); - const auto samples_render_pass_image = [&](const VideoCommon::ImageViewInOut& view) { - return view.id && - scheduler.IsRenderPassImage(texture_cache.GetImageView(view.id).ImageHandle()); - }; - if (scheduler.CanAliasRenderPass(texture_cache.GetFramebuffer()) && - std::ranges::any_of(view_span, samples_render_pass_image)) { - scheduler.RequestOutsideRenderPassOperationContext(); + if (buffer_cache.any_buffer_uploaded) { + buffer_cache.runtime.PostCopyBarrier(); + buffer_cache.any_buffer_uploaded = false; } + texture_cache.UpdateRenderTargets(false); + texture_cache.CheckFeedbackLoop(std::span{views.data(), + views.size()}); if (IsBuilt() && !pipeline) { return false; } @@ -630,12 +595,7 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, } } - if (buffer_cache.TakeDrawHazard()) { - scheduler.RequestOutsideRenderPassOperationContext(); - } - const AttachmentAccess access = MakeAttachmentAccess(maxwell3d->regs); - scheduler.RequestRenderpass(texture_cache.GetFramebuffer(), access.touched, access.written); - buffer_cache.CommitDrawWrites(); + scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); if (!is_built.load(std::memory_order::relaxed)) { // Wait for the pipeline to be built scheduler.Record([this](vk::CommandBuffer) { @@ -720,7 +680,7 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, return true; } -void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { +void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { FixedPipelineState::DynamicState dynamic{}; if (!key.state.extended_dynamic_state) { dynamic = key.state.dynamic_state; @@ -745,8 +705,7 @@ void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { if (instanced) { vertex_binding_divisors.push_back({ .binding = static_cast(index), - .divisor = (std::min)(key.state.binding_divisors[index], - device.GetMaxVertexAttribDivisor()), + .divisor = key.state.binding_divisors[index], }); } } @@ -780,7 +739,7 @@ void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { .vertexBindingDivisorCount = static_cast(vertex_binding_divisors.size()), .pVertexBindingDivisors = vertex_binding_divisors.data(), }; - if (!vertex_binding_divisors.empty() && device.IsExtVertexAttributeDivisorSupported()) { + if (!vertex_binding_divisors.empty()) { vertex_input_ci.pNext = &input_divisor_ci; } const bool has_tess_stages = spv_modules[1] || spv_modules[2]; @@ -949,8 +908,8 @@ void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { .stencilTestEnable = dynamic.stencil_enable, .front = GetStencilFaceState(dynamic.front), .back = GetStencilFaceState(dynamic.back), - .minDepthBounds = 0.0f, - .maxDepthBounds = 1.0f, + .minDepthBounds = f32(key.state.depth_bounds_min), + .maxDepthBounds = f32(key.state.depth_bounds_max), }; if (dynamic.depth_bounds_enable && !device.IsDepthBoundsSupported()) { LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported"); @@ -1114,10 +1073,9 @@ void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT; } - const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo(); pipeline = device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = &rendering_ci, + .pNext = nullptr, .flags = flags, .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), @@ -1131,6 +1089,7 @@ void GraphicsPipeline::MakePipeline(const RenderingFormats& formats) { .pColorBlendState = &color_blend_ci, .pDynamicState = &dynamic_state_ci, .layout = *pipeline_layout, + .renderPass = render_pass, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = 0, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index ba41dc2deb..b1ac5a3fcc 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -67,6 +67,7 @@ namespace Vulkan { class Device; class PipelineStatistics; +class RenderPassCache; class RescalingPushConstant; class RenderAreaPushConstant; class Scheduler; @@ -81,14 +82,11 @@ public: const Device& device, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, DescriptorBufferRing& descriptor_buffer_ring, Common::ThreadWorker* worker_thread, - PipelineStatistics* pipeline_statistics, const GraphicsPipelineCacheKey& key, - std::array stages, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key, std::array stages, const std::array& infos); bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; } - u32 VertexAttributeMask() const noexcept { - return vertex_attribute_mask; - } bool SupportsAlphaToCoverage() const noexcept { return fragment_has_color0_output; } @@ -142,7 +140,7 @@ private: bool ConfigureDraw(const RescalingPushConstant& rescaling, const RenderAreaPushConstant& render_are); - void MakePipeline(const RenderingFormats& formats); + void MakePipeline(VkRenderPass render_pass); void Validate(); @@ -171,7 +169,6 @@ private: size_t num_image_elements{}; u32 num_textures{}; bool fragment_has_color0_output{}; - u32 vertex_attribute_mask{}; vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; diff --git a/src/video_core/renderer_vulkan/vk_guest_memory.cpp b/src/video_core/renderer_vulkan/vk_guest_memory.cpp deleted file mode 100644 index 9b2fd20694..0000000000 --- a/src/video_core/renderer_vulkan/vk_guest_memory.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include - -#include "video_core/renderer_vulkan/vk_guest_memory.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/vulkan_common/vulkan_memory_allocator.h" - -namespace Vulkan { -namespace { -constexpr size_t PROBE_WORDS = 1024; -constexpr size_t PROBE_SIZE = PROBE_WORDS * sizeof(u32); -constexpr u32 PROBE_CPU_STRIDE = 0x9E3779B9U; -constexpr u32 PROBE_GPU_VALUE = 0x5AC3E10FU; -} - -GuestMemory::GuestMemory([[maybe_unused]] const Device& device, - MemoryAllocator& memory_allocator, Scheduler& scheduler, - [[maybe_unused]] const Common::HostMemory& host_memory) { - if (!windows.empty() && !IsCoherent(memory_allocator, scheduler)) { - windows.clear(); - } -} - -std::optional GuestMemory::Find(const u8* pointer, size_t size) const { - if (windows.empty() || pointer < base) { - return std::nullopt; - } - const size_t offset = static_cast(pointer - base); - const size_t index = offset / window_size; - const size_t local_offset = offset % window_size; - if (index >= windows.size() || window_size - local_offset < size) { - return std::nullopt; - } - return Range{*windows[index].buffer, local_offset}; -} - -bool GuestMemory::IsCoherent(MemoryAllocator& memory_allocator, Scheduler& scheduler) const { - const VkDeviceSize probe_offset = window_size - PROBE_SIZE; - u8* const probe = base + (windows.size() - 1) * window_size + probe_offset; - std::array saved; - std::memcpy(saved.data(), probe, PROBE_SIZE); - std::array pattern; - for (size_t index = 0; index < PROBE_WORDS; ++index) { - pattern[index] = static_cast(index + 1) * PROBE_CPU_STRIDE; - } - std::memcpy(probe, pattern.data(), PROBE_SIZE); - - vk::Buffer readback = memory_allocator.CreateBuffer( - VkBufferCreateInfo{ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = PROBE_SIZE, - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }, - MemoryUsage::Download); - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([src = *windows.back().buffer, dst = *readback, - probe_offset](vk::CommandBuffer cmdbuf) { - static constexpr VkMemoryBarrier2 READ_BEFORE_WRITE{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - }; - static constexpr VkMemoryBarrier2 WRITE_TO_HOST{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, - .dstAccessMask = VK_ACCESS_2_HOST_READ_BIT, - }; - const VkBufferCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, - .srcOffset = probe_offset, - .dstOffset = 0, - .size = PROBE_SIZE, - }; - cmdbuf.CopyBuffer(src, dst, copy); - cmdbuf.PipelineBarrier(READ_BEFORE_WRITE); - cmdbuf.FillBuffer(src, probe_offset, PROBE_SIZE, PROBE_GPU_VALUE); - cmdbuf.PipelineBarrier(WRITE_TO_HOST); - }); - scheduler.Finish(); - - const bool gpu_sees_cpu = - std::memcmp(readback.Mapped().data(), pattern.data(), PROBE_SIZE) == 0; - std::array written; - std::memcpy(written.data(), probe, PROBE_SIZE); - const bool cpu_sees_gpu = - std::ranges::all_of(written, [](u32 word) { return word == PROBE_GPU_VALUE; }); - std::memcpy(probe, saved.data(), PROBE_SIZE); - return gpu_sees_cpu && cpu_sees_gpu; -} - -} diff --git a/src/video_core/renderer_vulkan/vk_guest_memory.h b/src/video_core/renderer_vulkan/vk_guest_memory.h deleted file mode 100644 index ae206109ad..0000000000 --- a/src/video_core/renderer_vulkan/vk_guest_memory.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -#include "common/common_types.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Common { -class HostMemory; -} - -namespace Vulkan { - -class Device; -class MemoryAllocator; -class Scheduler; - -class GuestMemory { -public: - struct Range { - VkBuffer buffer; - VkDeviceSize offset; - }; - - explicit GuestMemory(const Device& device, MemoryAllocator& memory_allocator, - Scheduler& scheduler, const Common::HostMemory& host_memory); - - [[nodiscard]] std::optional Find(const u8* pointer, size_t size) const; - - [[nodiscard]] bool Empty() const noexcept { - return windows.empty(); - } - -private: - struct Window { - vk::DeviceMemory memory; - vk::ExternalBuffer buffer; - }; - - [[nodiscard]] bool IsCoherent(MemoryAllocator& memory_allocator, Scheduler& scheduler) const; - - std::vector windows; - u8* base{}; - size_t window_size{}; -}; - -} diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index e52e079969..be5a1144ce 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -121,9 +121,8 @@ VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuff } } -static constexpr VkPipelineStageFlags2 wait_stage_mask = - VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; +static constexpr VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, @@ -131,35 +130,130 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore wait_semaphore, u64 host_tick) { const VkSemaphore timeline_semaphore = *semaphore; - const std::array cmdbuffer_infos{{ - { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .pNext = nullptr, - .commandBuffer = *upload_cmdbuf, - .deviceMask = 0, - }, - { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .pNext = nullptr, - .commandBuffer = *cmdbuf, - .deviceMask = 0, - }, - }}; + if (device.HasSynchronization2()) { + const std::array cmdbuffer_infos{{ + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .pNext = nullptr, + .commandBuffer = *upload_cmdbuf, + .deviceMask = 0, + }, + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .pNext = nullptr, + .commandBuffer = *cmdbuf, + .deviceMask = 0, + }, + }}; - std::array signal_infos{{ - { + std::array signal_infos{{ + { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = timeline_semaphore, + .value = host_tick, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .deviceIndex = 0, + }, + {}, + }}; + u32 num_signal_semaphores = 1; + if (signal_semaphore) { + signal_infos[1] = VkSemaphoreSubmitInfo{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = signal_semaphore, + .value = 0, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .deviceIndex = 0, + }; + num_signal_semaphores = 2; + } + + const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; + const VkSemaphoreSubmitInfo wait_info{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .pNext = nullptr, - .semaphore = timeline_semaphore, - .value = host_tick, - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .semaphore = wait_semaphore, + .value = 0, + .stageMask = static_cast(wait_stage_mask), .deviceIndex = 0, - }, - {}, - }}; - u32 num_signal_semaphores = 1; - if (signal_semaphore) { - signal_infos[1] = VkSemaphoreSubmitInfo{ + }; + + const VkSubmitInfo2 submit_info2{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = num_wait_semaphores, + .pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr, + .commandBufferInfoCount = static_cast(cmdbuffer_infos.size()), + .pCommandBufferInfos = cmdbuffer_infos.data(), + .signalSemaphoreInfoCount = num_signal_semaphores, + .pSignalSemaphoreInfos = signal_infos.data(), + }; + return device.GetGraphicsQueue().Submit2(submit_info2); + } + + const u32 num_signal_semaphores = signal_semaphore ? 2 : 1; + const std::array signal_values{host_tick, u64(0)}; + const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; + + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + + const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; + // Pointers must be null when the count is zero (best-practices) + const VkSemaphore* p_wait_sems = + (num_wait_semaphores > 0) ? &wait_semaphore : nullptr; + const VkPipelineStageFlags* p_wait_masks = + (num_wait_semaphores > 0) ? &wait_stage_mask : nullptr; + const VkSemaphore* p_signal_sems = + (num_signal_semaphores > 0) ? signal_semaphores.data() : nullptr; + const u64 wait_zero = 0; // dummy for binary wait + const VkTimelineSemaphoreSubmitInfo timeline_si{ + .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreValueCount = num_wait_semaphores, + .pWaitSemaphoreValues = num_wait_semaphores ? &wait_zero : nullptr, + .signalSemaphoreValueCount = num_signal_semaphores, + .pSignalSemaphoreValues = signal_values.data(), + }; + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = &timeline_si, + .waitSemaphoreCount = num_wait_semaphores, + .pWaitSemaphores = p_wait_sems, + .pWaitDstStageMask = p_wait_masks, + .commandBufferCount = static_cast(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), + .signalSemaphoreCount = num_signal_semaphores, + .pSignalSemaphores = p_signal_sems, + }; + + return device.GetGraphicsQueue().Submit(submit_info); +} + +VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, + vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick) { + if (device.HasSynchronization2()) { + const std::array cmdbuffer_infos{{ + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .pNext = nullptr, + .commandBuffer = *upload_cmdbuf, + .deviceMask = 0, + }, + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .pNext = nullptr, + .commandBuffer = *cmdbuf, + .deviceMask = 0, + }, + }}; + + const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; + const VkSemaphoreSubmitInfo signal_info{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .pNext = nullptr, .semaphore = signal_semaphore, @@ -167,86 +261,66 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, .deviceIndex = 0, }; - num_signal_semaphores = 2; + + const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; + const VkSemaphoreSubmitInfo wait_info{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = wait_semaphore, + .value = 0, + .stageMask = static_cast(wait_stage_mask), + .deviceIndex = 0, + }; + + const VkSubmitInfo2 submit_info2{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = num_wait_semaphores, + .pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr, + .commandBufferInfoCount = static_cast(cmdbuffer_infos.size()), + .pCommandBufferInfos = cmdbuffer_infos.data(), + .signalSemaphoreInfoCount = num_signal_semaphores, + .pSignalSemaphoreInfos = num_signal_semaphores ? &signal_info : nullptr, + }; + + auto fence = GetFreeFence(); + auto result = device.GetGraphicsQueue().Submit2(submit_info2, *fence); + + if (result == VK_SUCCESS) { + std::scoped_lock lock{wait_mutex}; + wait_queue.emplace(host_tick, std::move(fence)); + wait_cv.notify_one(); + } + + return result; } - const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; - const VkSemaphoreSubmitInfo wait_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = wait_semaphore, - .value = 0, - .stageMask = wait_stage_mask, - .deviceIndex = 0, - }; - - const VkSubmitInfo2 submit_info2{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, - .pNext = nullptr, - .flags = 0, - .waitSemaphoreInfoCount = num_wait_semaphores, - .pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr, - .commandBufferInfoCount = static_cast(cmdbuffer_infos.size()), - .pCommandBufferInfos = cmdbuffer_infos.data(), - .signalSemaphoreInfoCount = num_signal_semaphores, - .pSignalSemaphoreInfos = signal_infos.data(), - }; - return device.GetGraphicsQueue().Submit(submit_info2); -} - -VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, - vk::CommandBuffer& upload_cmdbuf, - VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, - u64 host_tick) { - const std::array cmdbuffer_infos{{ - { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .pNext = nullptr, - .commandBuffer = *upload_cmdbuf, - .deviceMask = 0, - }, - { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .pNext = nullptr, - .commandBuffer = *cmdbuf, - .deviceMask = 0, - }, - }}; - const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; - const VkSemaphoreSubmitInfo signal_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = signal_semaphore, - .value = 0, - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, - .deviceIndex = 0, - }; - const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; - const VkSemaphoreSubmitInfo wait_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = wait_semaphore, - .value = 0, - .stageMask = wait_stage_mask, - .deviceIndex = 0, - }; - const VkSubmitInfo2 submit_info2{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + const VkSemaphore* p_wait_sems = + (num_wait_semaphores > 0) ? &wait_semaphore : nullptr; + const VkPipelineStageFlags* p_wait_masks = + (num_wait_semaphores > 0) ? &wait_stage_mask : nullptr; + const VkSemaphore* p_signal_sems = + (num_signal_semaphores > 0) ? &signal_semaphore : nullptr; + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, - .flags = 0, - .waitSemaphoreInfoCount = num_wait_semaphores, - .pWaitSemaphoreInfos = num_wait_semaphores ? &wait_info : nullptr, - .commandBufferInfoCount = static_cast(cmdbuffer_infos.size()), - .pCommandBufferInfos = cmdbuffer_infos.data(), - .signalSemaphoreInfoCount = num_signal_semaphores, - .pSignalSemaphoreInfos = num_signal_semaphores ? &signal_info : nullptr, + .waitSemaphoreCount = num_wait_semaphores, + .pWaitSemaphores = p_wait_sems, + .pWaitDstStageMask = p_wait_masks, + .commandBufferCount = static_cast(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), + .signalSemaphoreCount = num_signal_semaphores, + .pSignalSemaphores = p_signal_sems, }; auto fence = GetFreeFence(); - auto result = device.GetGraphicsQueue().Submit(submit_info2, *fence); + auto result = device.GetGraphicsQueue().Submit(submit_info, *fence); if (result == VK_SUCCESS) { std::scoped_lock lock{wait_mutex}; @@ -279,7 +353,7 @@ void MasterSemaphore::WaitThread(std::stop_token token) { free_queue.push_front(std::move(fence)); gpu_tick.store(host_tick, std::memory_order_release); } - gpu_tick.notify_all(); + gpu_tick.notify_one(); } } diff --git a/src/video_core/renderer_vulkan/vk_multi_range_buffer.cpp b/src/video_core/renderer_vulkan/vk_multi_range_buffer.cpp index 68dcab9250..4d84390002 100644 --- a/src/video_core/renderer_vulkan/vk_multi_range_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_multi_range_buffer.cpp @@ -32,6 +32,8 @@ MultiRangeBufferCache::MultiRangeBufferCache(const Device& device) { VkDeviceSize MultiRangeBufferCache::QueryBlockSize(const Device& device, u32& memory_type_bits) const { + const VkDevice logical = *device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); const VkBufferCreateInfo probe_ci{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -42,14 +44,6 @@ VkDeviceSize MultiRangeBufferCache::QueryBlockSize(const Device& device, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, }; - if (device.IsKhrMaintenance4Supported()) { - const VkMemoryRequirements reqs = - device.GetLogical().GetDeviceBufferMemoryRequirements(probe_ci); - memory_type_bits = reqs.memoryTypeBits; - return reqs.alignment; - } - const VkDevice logical = *device.GetLogical(); - const auto& dld = device.GetDispatchLoader(); VkBuffer probe{}; if (dld.vkCreateBuffer(logical, &probe_ci, nullptr, &probe) != VK_SUCCESS) { return 0; @@ -87,7 +81,7 @@ u64 MultiRangeBufferCache::HashSources(std::span sources u64 MultiRangeBufferCache::HashContent(std::span sources) const { u64 hash = 0xcbf29ce484222325ULL; for (const MultiRangeSource& source : sources) { - hash ^= source.content_serial; + hash ^= source.write_tick; hash *= 0x100000001b3ULL; } return hash; @@ -219,6 +213,9 @@ MultiRangeRef MultiRangeBufferCache::Get(const Device& device, Scheduler& schedu if (sources.empty() || total == 0) { return MultiRangeRef{}; } + if (!retired.empty()) { + DrainRetired(scheduler); + } const u64 geometry = HashSources(sources); const u64 content = HashContent(sources); const auto it = entries.find(key); @@ -332,4 +329,10 @@ void MultiRangeBufferCache::DropOwner(Scheduler& scheduler, VkBuffer owner) { } } +void MultiRangeBufferCache::Invalidate(u64 key) { + if (auto const it = entries.find(key); it != entries.end()) { + it->second.dirty = true; + } +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_multi_range_buffer.h b/src/video_core/renderer_vulkan/vk_multi_range_buffer.h index d9c065df3f..f40d3aa1f8 100644 --- a/src/video_core/renderer_vulkan/vk_multi_range_buffer.h +++ b/src/video_core/renderer_vulkan/vk_multi_range_buffer.h @@ -27,7 +27,7 @@ struct MultiRangeSource { VkDeviceSize memory_offset{}; VkDeviceSize offset{}; VkDeviceSize size{}; - u64 content_serial{}; + u64 write_tick{}; u32 memory_type{}; }; @@ -55,9 +55,9 @@ public: void MarkGathered(u64 key); - void DropOwner(Scheduler& scheduler, VkBuffer owner); + void Invalidate(u64 key); - void DrainRetired(Scheduler& scheduler); + void DropOwner(Scheduler& scheduler, VkBuffer owner); VkDeviceSize block_size{DEFAULT_BLOCK_SIZE}; bool use_sparse{}; @@ -94,6 +94,8 @@ private: void RetireEntry(Scheduler& scheduler, Entry& entry); + void DrainRetired(Scheduler& scheduler); + ::Common::unordered_map entries; boost::container::static_vector retired; u32 sparse_memory_type_bits{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index dd55b576e8..d4ffcd8317 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -23,6 +23,7 @@ #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/translate_program.h" +#include "shader_recompiler/ir_opt/passes.h" #include "shader_recompiler/program_header.h" #include "video_core/engines/kepler_compute.h" #include "video_core/engines/maxwell_3d.h" @@ -62,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 20; +constexpr u32 CACHE_VERSION = 18; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -343,11 +344,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, DescriptorPool& descriptor_pool_, GuestDescriptorQueue& guest_descriptor_queue_, DescriptorBufferRing& descriptor_buffer_ring_, - BufferCache& buffer_cache_, TextureCache& texture_cache_, - VideoCore::ShaderNotify& shader_notify_) + RenderPassCache& render_pass_cache_, BufferCache& buffer_cache_, + TextureCache& texture_cache_, VideoCore::ShaderNotify& shader_notify_) : VideoCommon::ShaderCache{device_memory_}, device{device_}, scheduler{scheduler_}, descriptor_pool{descriptor_pool_}, guest_descriptor_queue{guest_descriptor_queue_}, - descriptor_buffer_ring{descriptor_buffer_ring_}, buffer_cache{buffer_cache_}, + descriptor_buffer_ring{descriptor_buffer_ring_}, + render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, @@ -445,7 +447,9 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS || - driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, + driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || + driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_MESA_TURNIP, .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, .has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, @@ -551,8 +555,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.SupportsProvokingVertexLastMode(); dynamic_features.has_provoking_vertex_tf_preserve = device.SupportsTransformFeedbackProvokingVertexPreservation(); - dynamic_features.has_dynamic_rendering_unused_attachments = - device.IsExtDynamicRenderingUnusedAttachmentsSupported(); } PipelineCache::~PipelineCache() { @@ -568,7 +570,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { current_pipeline = nullptr; return nullptr; } - graphics_key.state.Refresh(*maxwell3d, dynamic_features, fragment_color_outputs); + graphics_key.state.Refresh(*maxwell3d, dynamic_features); if (current_pipeline) { GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; @@ -787,6 +789,11 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const if (!use_asynchronous_shaders) { return pipeline; } + // If something is using depth, we can assume that games are not rendering anything which + // will be used one time. + if (maxwell3d->regs.zeta_enable) { + return nullptr; + } // If games are using a small index count, we can assume these are full screen quads. // Usually these shaders are only used once for building textures so we can assume they // can't be built async @@ -838,6 +845,11 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + if (device.GetDriverID() != VK_DRIVER_ID_NVIDIA_PROPRIETARY && Shader::Optimization::HasBrokenPattern(programs[index])) { + LOG_WARNING(Render_Vulkan, "0x{:016x}: Skipping shader with known issues", key.unique_hashes[index]); + return nullptr; + } + if (Settings::values.dump_guest_shaders) { env.Dump(hash, key.unique_hashes[index]); } @@ -898,7 +910,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( return std::make_unique( scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, descriptor_pool, guest_descriptor_queue, descriptor_buffer_ring, thread_worker, statistics, - key, std::move(modules), infos); + render_pass_cache, key, std::move(modules), infos); } catch (const Shader::Exception& exception) { auto hash = key.Hash(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index bebac7cb15..71dfb46e3d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -85,6 +85,7 @@ class ComputePipeline; class DescriptorPool; class Device; class PipelineStatistics; +class RenderPassCache; class Scheduler; using VideoCommon::ShaderInfo; @@ -106,7 +107,8 @@ public: explicit PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device, Scheduler& scheduler, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, - DescriptorBufferRing& descriptor_buffer_ring, BufferCache& buffer_cache, + DescriptorBufferRing& descriptor_buffer_ring, + RenderPassCache& render_pass_cache, BufferCache& buffer_cache, TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_); ~PipelineCache(); @@ -151,6 +153,7 @@ private: DescriptorPool& descriptor_pool; GuestDescriptorQueue& guest_descriptor_queue; DescriptorBufferRing& descriptor_buffer_ring; + RenderPassCache& render_pass_cache; BufferCache& buffer_cache; TextureCache& texture_cache; VideoCore::ShaderNotify& shader_notify; diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 52d497e446..9298764546 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -7,6 +7,9 @@ #include "common/settings.h" #include "common/thread.h" #include "core/frontend/emu_window.h" +#ifdef HAS_LSFG +#include "video_core/renderer_vulkan/present/lsfg_common.h" +#endif #include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_swapchain.h" @@ -21,6 +24,18 @@ namespace { constexpr size_t MAX_FRAMES_IN_FLIGHT = 7; constexpr u32 MAX_PRESENT_ATTEMPTS = 3; +#ifdef HAS_LSFG +static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS); +#endif + +bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) { +#ifdef HAS_LSFG + const VkFormatProperties props{physical_device.GetFormatProperties(format)}; + return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0; +#else + return false; +#endif +} bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) { const VkFormatProperties props{physical_device.GetFormatProperties(format)}; @@ -36,11 +51,9 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat form }; } -[[nodiscard]] VkImageBlit2 MakeImageBlit(s32 frame_width, s32 frame_height, s32 swapchain_width, - s32 swapchain_height) { - return VkImageBlit2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr, +[[nodiscard]] VkImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { + return VkImageBlit{ .srcSubresource = MakeImageSubresourceLayers(), .srcOffsets = { @@ -72,11 +85,9 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat form }; } -[[nodiscard]] VkImageCopy2 MakeImageCopy(u32 frame_width, u32 frame_height, u32 swapchain_width, - u32 swapchain_height) { - return VkImageCopy2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2, - .pNext = nullptr, +[[nodiscard]] VkImageCopy MakeImageCopy(u32 frame_width, u32 frame_height, u32 swapchain_width, + u32 swapchain_height) { + return VkImageCopy{ .srcSubresource = MakeImageSubresourceLayers(), .srcOffset = { @@ -117,8 +128,8 @@ PresentManager::PresentManager(const vk::Instance& instance_, , swapchain{swapchain_} , surface{surface_} , blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())} - , use_present_thread{Settings::values.async_presentation.GetValue() || - Settings::values.frame_gen.GetValue()} + , storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())} + , use_present_thread{Settings::values.async_presentation.GetValue()} { SetImageCount(); @@ -147,6 +158,7 @@ PresentManager::PresentManager(const vk::Instance& instance_, .pNext = nullptr, .flags = VK_FENCE_CREATE_SIGNALED_BIT, }); + frame.storage_capable = storage_supported; free_queue.push_back(&frame); } @@ -174,20 +186,7 @@ Frame* PresentManager::GetRenderFrame() { return frame; } -Frame* PresentManager::TryGetRenderFrame() { - std::scoped_lock lock{free_mutex}; - if (free_queue.size() < 2 || free_queue.front()->present_done.GetStatus() != VK_SUCCESS) { - return nullptr; - } - Frame* const frame = free_queue.front(); - free_queue.pop_front(); - frame->present_done.Reset(); - return frame; -} - -void PresentManager::Present(Frame* frame, u32 pace_index, std::chrono::nanoseconds pace_step) { - frame->pace_index = pace_index; - frame->pace_step = pace_step; +void PresentManager::Present(Frame* frame) { if (use_present_thread) { scheduler.Record([this, frame](vk::CommandBuffer) { std::unique_lock lock{queue_mutex}; @@ -205,13 +204,23 @@ size_t PresentManager::MaxExtraFrames() const { return image_count - 1; } -void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, - VkFormat image_view_format) { +bool PresentManager::NeedsStorage(const Frame* frame, bool required) const { + return required && frame->storage_capable && !frame->storage_view; +} + +void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format, + VkRenderPass rd, bool storage) { auto& dld = device.GetLogical(); frame->width = width; frame->height = height; + const bool with_storage = storage && frame->storage_capable; + VkImageUsageFlags storage_usage = 0; + if (with_storage) { + storage_usage = VK_IMAGE_USAGE_STORAGE_BIT; + } + frame->image = memory_allocator.CreateImage({ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -229,7 +238,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | storage_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -259,6 +268,46 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, .layerCount = 1, }, }); + + frame->storage_view = vk::ImageView{}; + if (with_storage) { + frame->storage_view = dld.CreateImageView({ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = *frame->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = swapchain.GetImageFormat(), + .components = + { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }); + } + + const VkImageView image_view{*frame->image_view}; + frame->framebuffer = dld.CreateFramebuffer({ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = rd, + .attachmentCount = 1, + .pAttachments = &image_view, + .width = width, + .height = height, + .layers = 1, + }); } void PresentManager::WaitPresent() { @@ -282,33 +331,21 @@ void PresentManager::PresentThread(std::stop_token token) { Common::SetCurrentThreadName("VulkanPresent"); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); Common::SetCurrentThreadToPerformanceCores(); - std::chrono::steady_clock::time_point pace_anchor{}; while (!token.stop_requested()) { std::unique_lock lock{queue_mutex}; // Wait for presentation frames frame_cv.wait(lock, token, [this] { return !present_queue.empty(); }); - if (!present_queue.empty() && present_queue.front()->pace_index != 0) { - const Frame* const held = present_queue.front(); - const auto next_anchor = [this] { - return std::any_of(present_queue.begin() + 1, present_queue.end(), - [](const Frame* next) { return next->pace_index == 0; }); - }; - void(frame_cv.wait_until(lock, token, - pace_anchor + held->pace_step * held->pace_index, - next_anchor)); - } if (!token.stop_requested()) { // Take the frame and notify anyone waiting Frame* frame = present_queue.front(); present_queue.pop_front(); frame_cv.notify_one(); + // By exchanging the lock ownership we take the swapchain lock + // before the queue lock goes out of scope. This way the swapchain + // lock in WaitPresent is guaranteed to occur after here. void(std::exchange(lock, std::unique_lock{swapchain_mutex})); CopyToSwapchain(frame); - if (frame->pace_index == 0 && frame->pace_step.count() != 0) { - frame->present_done.Wait(static_cast(frame->pace_step.count())); - pace_anchor = std::chrono::steady_clock::now(); - } // Free the frame for reuse std::scoped_lock fl{free_mutex}; @@ -330,9 +367,9 @@ void PresentManager::SetImageCount() { #ifdef HAS_LSFG const size_t generations = Settings::FrameGenMaxGenerations(); const size_t queued_composites = Settings::values.frame_gen_queue_target.GetValue() + 1; - const size_t baseline = swapchain.GetImageCount() + generations; - image_count = std::min((std::max)((generations + 1) * queued_composites, baseline), - MAX_FRAMES_IN_FLIGHT); + image_count = + std::clamp((generations + 1) * queued_composites, swapchain.GetImageCount(), + MAX_FRAMES_IN_FLIGHT); #else image_count = std::min(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT); #endif @@ -341,24 +378,18 @@ void PresentManager::SetImageCount() { } void PresentManager::DiscardFrame(Frame* frame) { - const VkSemaphoreSubmitInfo wait_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + static constexpr VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + const VkSemaphore render_ready = *frame->render_ready; + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, - .semaphore = *frame->render_ready, - .value = 0, - .stageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, - .deviceIndex = 0, - }; - const VkSubmitInfo2 submit_info{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, - .pNext = nullptr, - .flags = 0, - .waitSemaphoreInfoCount = 1U, - .pWaitSemaphoreInfos = &wait_info, - .commandBufferInfoCount = 0U, - .pCommandBufferInfos = nullptr, - .signalSemaphoreInfoCount = 0U, - .pSignalSemaphoreInfos = nullptr, + .waitSemaphoreCount = 1U, + .pWaitSemaphores = &render_ready, + .pWaitDstStageMask = &wait_stage, + .commandBufferCount = 0U, + .pCommandBuffers = nullptr, + .signalSemaphoreCount = 0U, + .pSignalSemaphores = nullptr, }; std::scoped_lock submit_lock{scheduler.submit_mutex}; @@ -418,13 +449,11 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { const VkImage image{swapchain.CurrentImage()}; const VkExtent2D extent = swapchain.GetExtent(); const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -438,13 +467,11 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -460,13 +487,11 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { }, }; const std::array post_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_NONE, - .dstAccessMask = VK_ACCESS_2_NONE, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -480,13 +505,11 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -502,7 +525,8 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { }, }; - cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, {}, + {}, {}, pre_barriers); if (blit_supported) { cmdbuf.BlitImage(*frame->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, @@ -515,55 +539,30 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { MakeImageCopy(frame->width, frame->height, extent.width, extent.height)); } - cmdbuf.PipelineBarrier(0, {}, {}, post_barriers); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {}, + {}, {}, post_barriers); cmdbuf.End(); const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore(); const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore(); - const std::array wait_infos{{ - { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = present_semaphore, - .value = 0, - .stageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .deviceIndex = 0, - }, - { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = *frame->render_ready, - .value = 0, - .stageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .deviceIndex = 0, - }, - }}; - const VkCommandBufferSubmitInfo cmdbuf_info{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .pNext = nullptr, - .commandBuffer = *cmdbuf, - .deviceMask = 0, - }; - const VkSemaphoreSubmitInfo signal_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .pNext = nullptr, - .semaphore = render_semaphore, - .value = 0, - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, - .deviceIndex = 0, + const std::array wait_semaphores = {present_semaphore, *frame->render_ready}; + + static constexpr std::array wait_stage_masks{ + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, }; - const VkSubmitInfo2 submit_info{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, - .flags = 0, - .waitSemaphoreInfoCount = 2U, - .pWaitSemaphoreInfos = wait_infos.data(), - .commandBufferInfoCount = 1U, - .pCommandBufferInfos = &cmdbuf_info, - .signalSemaphoreInfoCount = 1U, - .pSignalSemaphoreInfos = &signal_info, + .waitSemaphoreCount = 2U, + .pWaitSemaphores = wait_semaphores.data(), + .pWaitDstStageMask = wait_stage_masks.data(), + .commandBufferCount = 1, + .pCommandBuffers = cmdbuf.address(), + .signalSemaphoreCount = 1U, + .pSignalSemaphores = &render_semaphore, }; // Submit the image copy/blit to the swapchain diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h index 7e1a47bb45..ab2b76f10f 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.h +++ b/src/video_core/renderer_vulkan/vk_present_manager.h @@ -7,7 +7,6 @@ #pragma once #include -#include #include #include #include @@ -33,11 +32,12 @@ struct Frame { u32 index; vk::Image image; vk::ImageView image_view; + vk::ImageView storage_view; + vk::Framebuffer framebuffer; vk::CommandBuffer cmdbuf; vk::Semaphore render_ready; vk::Fence present_done; - std::chrono::nanoseconds pace_step{}; - u32 pace_index{}; + bool storage_capable{}; }; class PresentManager { @@ -54,13 +54,14 @@ public: /// Returns the last used presentation frame Frame* GetRenderFrame(); - [[nodiscard]] Frame* TryGetRenderFrame(); - /// Pushes a frame for presentation - void Present(Frame* frame, u32 pace_index, std::chrono::nanoseconds pace_step); + void Present(Frame* frame); /// Recreates the present frame to match the provided parameters - void RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format); + void RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format, + VkRenderPass rd, bool storage); + + [[nodiscard]] bool NeedsStorage(const Frame* frame, bool required) const; /// Waits for the present thread to finish presenting all queued frames. void WaitPresent(); @@ -103,6 +104,7 @@ private: std::mutex free_mutex; std::jthread present_thread; bool blit_supported; + bool storage_supported; bool use_present_thread; std::atomic image_count{}; std::atomic swapchain_image_count{}; diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 598bbd3633..f63c924e39 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -37,12 +37,6 @@ using Tegra::Engines::Maxwell3D; using VideoCommon::QueryType; namespace { -bool LacksQueryWriteBack(const Device& device) { - const VkDriverId driver_id = device.GetDriverID(); - return driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || - driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; -} - class SamplesQueryBank : public VideoCommon::BankBase { public: static constexpr size_t BANK_SIZE = 256; @@ -241,7 +235,9 @@ public: return; } PauseCounter(); - if (LacksQueryWriteBack(device)) { + const auto driver_id = device.GetDriverID(); + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -264,13 +260,11 @@ public: scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([start, amount, base_offset, query_pool, buffer = *resolve_buffer](vk::CommandBuffer cmdbuf) { - const VkBufferMemoryBarrier2 copy_query_pool_barrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + const VkBufferMemoryBarrier copy_query_pool_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = buffer, @@ -282,7 +276,8 @@ public: query_pool, static_cast(start), static_cast(amount), buffer, static_cast(base_offset), SamplesQueryBank::QUERY_SIZE, VK_QUERY_RESULT_WAIT_BIT | VK_QUERY_RESULT_64_BIT); - cmdbuf.PipelineBarrier(copy_query_pool_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, copy_query_pool_barrier); }); offsets[bank_id] = {start, base_offset}; base_offset += amount * SamplesQueryBank::QUERY_SIZE; @@ -657,13 +652,11 @@ public: scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([this, dst_buffer = stagging_buffer.buffer, extra_offset, start, size](vk::CommandBuffer cmdbuf) { - const VkBufferCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, + std::array copy{VkBufferCopy{ .srcOffset = start * QUERY_SIZE, .dstOffset = extra_offset, .size = size * QUERY_SIZE, - }; + }}; cmdbuf.CopyBuffer(*buffer, dst_buffer, copy); }); } @@ -821,7 +814,7 @@ public: new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; } - + scheduler.RequestOutsideRenderPassOperationContext(); CloseCounter(); auto [bank_slot, data_slot] = ProduceCounterBuffer(slot); @@ -869,17 +862,16 @@ public: offset_base += TFBQueryBank::QUERY_SIZE; bank.CloseReference(); } - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_HOST, - .dstAccessMask = VK_ACCESS_2_HOST_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(WRITE_BARRIER); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + vk::PIPELINE_STAGE_HOST, 0, WRITE_BARRIER); }); std::scoped_lock lk(flush_guard); @@ -936,7 +928,16 @@ private: }); return; } + static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + }; scheduler.Record([this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0, + COUNTER_RESUME_BARRIER); cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); }); } @@ -951,8 +952,6 @@ private: } has_flushed_end_pending = false; - // Refresh buffer state before ending transform feedback to ensure counters_count is up-to-date. - UpdateBuffers(); if (buffers_count == 0) { LOG_DEBUG(Render_Vulkan, "EndTransformFeedbackEXT called with no counters (buffers_count=0)"); scheduler.Record([](vk::CommandBuffer cmdbuf) { @@ -960,7 +959,6 @@ private: }); } else { LOG_DEBUG(Render_Vulkan, "EndTransformFeedbackEXT called with counters (buffers_count={})", buffers_count); - scheduler.MarkRenderPassWrites(); scheduler.Record([this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); @@ -1024,37 +1022,33 @@ private: const size_t slot = other; // workaround to compile bug. current_bank->AddReference(); - static constexpr VkMemoryBarrier2 READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, - .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, }; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_buffer = current_bank->GetBuffer(), src_buffer = counter_buffers[slot_index], src_offset = offsets[slot_index], slot](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(READ_BARRIER); - const VkBufferCopy2 copy{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); + std::array copy{VkBufferCopy{ .srcOffset = src_offset, .dstOffset = slot * TFBQueryBank::QUERY_SIZE, .size = TFBQueryBank::QUERY_SIZE, - }; + }}; cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy); - cmdbuf.PipelineBarrier(WRITE_BARRIER); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, WRITE_BARRIER); }); return {current_bank_id, slot}; } @@ -1347,7 +1341,7 @@ struct QueryCacheRuntimeImpl { std::vector> little_cache; std::vector> buffers_to_upload_to; std::vector redirect_cache; - std::vector> copies_setup; + std::vector> copies_setup; // Host conditional rendering data std::optional conditional_resolve_pass; @@ -1410,10 +1404,6 @@ void QueryCacheRuntime::ResumeHostConditionalRendering() { impl->is_hcr_running = true; } -bool QueryCacheRuntime::IsHostConditionalRenderingSet() const noexcept { - return impl->hcr_is_set; -} - void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal) { { @@ -1425,19 +1415,24 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo impl->hcr_buffer = buffer->Handle(); impl->hcr_offset = offset; } - VkConditionalRenderingFlagsEXT flags = 0; - if (is_equal) { - flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; + if (impl->hcr_is_set) { + if (impl->hcr_setup.buffer == impl->hcr_buffer && + impl->hcr_setup.offset == impl->hcr_offset) { + return; + } } - if (impl->hcr_is_set && impl->hcr_setup.buffer == impl->hcr_buffer && - impl->hcr_setup.offset == impl->hcr_offset && impl->hcr_setup.flags == flags) { - return; + bool was_running = impl->is_hcr_running; + if (was_running) { + PauseHostConditionalRendering(); } - PauseHostConditionalRendering(); impl->hcr_setup.buffer = impl->hcr_buffer; impl->hcr_setup.offset = impl->hcr_offset; - impl->hcr_setup.flags = flags; + impl->hcr_setup.flags = is_equal ? VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT : 0; impl->hcr_is_set = true; + impl->is_hcr_running = false; + if (was_running) { + ResumeHostConditionalRendering(); + } } void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal, @@ -1454,16 +1449,20 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo to_resolve = buffer->Handle(); to_resolve_offset = static_cast(offset); } - PauseHostConditionalRendering(); + bool was_running = impl->is_hcr_running; + if (was_running) { + PauseHostConditionalRendering(); + } impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve, to_resolve_offset, compare_to_zero); impl->hcr_setup.buffer = *impl->hcr_resolve_buffer; impl->hcr_setup.offset = 0; - impl->hcr_setup.flags = 0; - if (!is_equal) { - impl->hcr_setup.flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; - } + impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; impl->hcr_is_set = true; + impl->is_hcr_running = false; + if (was_running) { + ResumeHostConditionalRendering(); + } } bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1, @@ -1471,10 +1470,6 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup if (!impl->device.IsExtConditionalRendering()) { return false; } - if (LacksQueryWriteBack(impl->device)) { - EndHostConditionalRendering(); - return true; - } HostConditionalRenderingCompareBCImpl(object_1.address, true, true); return true; } @@ -1524,8 +1519,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || - LacksQueryWriteBack(impl->device)) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } @@ -1577,24 +1571,30 @@ VideoCommon::StreamerInterface* QueryCacheRuntime::GetStreamerInterface(QueryTyp } void QueryCacheRuntime::Barriers(bool is_prebarrier) { - VkMemoryBarrier2 barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = impl->device.GetBufferConsumerStages(), - .dstAccessMask = impl->device.GetBufferConsumerAccess(), + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }; + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - if (is_prebarrier) { - barrier.srcStageMask = impl->device.GetBufferUserStages(); - barrier.srcAccessMask = vk::ACCESS_BUFFER_WRITES; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - barrier.dstAccessMask = vk::ACCESS_TRANSFER; - } impl->scheduler.RequestOutsideRenderPassOperationContext(); - impl->scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(barrier); - }); + if (is_prebarrier) { + impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); + }); + } else { + impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_HOST, 0, WRITE_BARRIER); + }); + } } template @@ -1646,7 +1646,6 @@ void QueryCacheRuntime::SyncValues(std::span values, VkBuffer ba const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto [buffer, offset] = impl->buffer_cache.ObtainCPUBuffer( pair.first, static_cast(pair.second - pair.first), sync_info, post_op); - buffer->MarkContentModified(); impl->buffers_to_upload_to.emplace_back(buffer->Handle(), offset); } }); @@ -1661,9 +1660,7 @@ void QueryCacheRuntime::SyncValues(std::span values, VkBuffer ba size_t accumulated_size = 0; for (size_t i = 0; i < values.size(); i++) { size_t which_copy = impl->redirect_cache[i]; - impl->copies_setup[which_copy].emplace_back(VkBufferCopy2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, + impl->copies_setup[which_copy].emplace_back(VkBufferCopy{ .srcOffset = current_offset + accumulated_size, .dstOffset = impl->buffers_to_upload_to[which_copy].second + values[i].address - impl->little_cache[which_copy].first, @@ -1677,9 +1674,7 @@ void QueryCacheRuntime::SyncValues(std::span values, VkBuffer ba } else { for (size_t i = 0; i < values.size(); i++) { size_t which_copy = impl->redirect_cache[i]; - impl->copies_setup[which_copy].emplace_back(VkBufferCopy2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, - .pNext = nullptr, + impl->copies_setup[which_copy].emplace_back(VkBufferCopy{ .srcOffset = values[i].offset, .dstOffset = impl->buffers_to_upload_to[which_copy].second + values[i].address - impl->little_cache[which_copy].first, diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index f89038f8b7..bbb5234e11 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h @@ -48,8 +48,6 @@ public: void ResumeHostConditionalRendering(); - [[nodiscard]] bool IsHostConditionalRenderingSet() const noexcept; - bool HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1, bool qc_dirty); bool HostConditionalRenderingCompareValues(VideoCommon::LookupData object_1, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a7bf7aecfd..264ffb28b5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -166,11 +166,7 @@ DrawParams MakeDrawParams(const Tegra::Engines::Maxwell3D::DrawManager::State& d params.base_vertex = 0; params.is_indexed = true; } else if (draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { - u32 strips = 0; - if (params.num_vertices >= 2) { - strips = (params.num_vertices - 2) / 2; - } - params.num_vertices = strips * 6; + params.num_vertices = (params.num_vertices - 2) / 2 * 6; params.base_vertex = 0; params.is_indexed = true; } @@ -206,15 +202,15 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra StateTracker& state_tracker_, Scheduler& scheduler_) : gpu{gpu_}, device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, - staging_pool(device, memory_allocator, scheduler), + staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), guest_descriptor_queue(device, UpdateDescriptorQueue::GUEST_FRAME_PAYLOAD_SIZE, device.IsExtDescriptorBufferSupported()), compute_pass_descriptor_queue(device, UpdateDescriptorQueue::COMPUTE_FRAME_PAYLOAD_SIZE), descriptor_buffer_ring(device, memory_allocator), - blit_image(device, scheduler, state_tracker, descriptor_pool), - texture_cache_runtime{device, scheduler, memory_allocator, - staging_pool, blit_image, descriptor_pool, - compute_pass_descriptor_queue}, + blit_image(device, scheduler, state_tracker, descriptor_pool), render_pass_cache(device), + texture_cache_runtime{ + device, scheduler, memory_allocator, staging_pool, + blit_image, render_pass_cache, descriptor_pool, compute_pass_descriptor_queue}, texture_cache(texture_cache_runtime, device_memory), buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, guest_descriptor_queue, compute_pass_descriptor_queue, descriptor_pool), @@ -223,13 +219,12 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra staging_pool, compute_pass_descriptor_queue, descriptor_pool, texture_cache), query_cache(gpu, *this, device_memory, query_cache_runtime), pipeline_cache(device_memory, device, scheduler, descriptor_pool, guest_descriptor_queue, - descriptor_buffer_ring, buffer_cache, texture_cache, gpu.ShaderNotify()), + descriptor_buffer_ring, render_pass_cache, buffer_cache, texture_cache, + gpu.ShaderNotify()), accelerate_dma(buffer_cache, texture_cache, scheduler), - fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler) { + fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), + wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); - if (Settings::values.use_unified_memory.GetValue()) { - texture_cache_runtime.ImportGuestMemory(device_memory.GetHostMemory()); - } } RasterizerVulkan::~RasterizerVulkan() { @@ -238,7 +233,7 @@ RasterizerVulkan::~RasterizerVulkan() { } template -void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw_func) { +void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { SCOPE_EXIT { gpu.TickWork(); @@ -246,17 +241,6 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw FlushWork(); gpu_memory->FlushCaching(); - if (skip_empty) { - const auto& draw_state = maxwell3d->draw_manager.draw_state; - u32 count = draw_state.vertex_buffer.count; - if (is_indexed) { - count = draw_state.index_buffer.count; - } - if (count == 0) { - return; - } - } - GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; if (!pipeline) { return; @@ -267,7 +251,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw if (!pipeline->Configure(is_indexed)) return; - UpdateDynamicStates(*pipeline); + UpdateDynamicStates(); query_cache.NotifySegment(true); HandleTransformFeedback(); @@ -276,7 +260,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw } void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { - PrepareDraw(is_indexed, true, [this, is_indexed, instance_count] { + PrepareDraw(is_indexed, [this, is_indexed, instance_count] { const auto& draw_state = maxwell3d->draw_manager.draw_state; const u32 num_instances{instance_count}; const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; @@ -311,13 +295,10 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { void RasterizerVulkan::DrawIndirect() { const auto& params = maxwell3d->draw_manager.indirect_state; buffer_cache.SetDrawIndirect(¶ms); - PrepareDraw(params.is_indexed, false, [this, ¶ms] { + PrepareDraw(params.is_indexed, [this, ¶ms] { const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer(); const auto& buffer = indirect_buffer.first; const auto& offset = indirect_buffer.second; - VkBuffer command_buffer = buffer->Handle(); - VkDeviceSize command_offset = offset; - u32 command_stride = static_cast(params.stride); if (params.is_byte_count) { scheduler.Record([buffer_obj = buffer->Handle(), offset, stride = params.stride](vk::CommandBuffer cmdbuf) { @@ -327,38 +308,32 @@ void RasterizerVulkan::DrawIndirect() { return; } if (params.include_count) { - if (!device.IsDrawIndirectCountSupported()) { - return; - } const auto count = buffer_cache.GetDrawIndirectCount(); const auto& draw_buffer = count.first; const auto& offset_base = count.second; - scheduler.Record([draw_buffer_obj = draw_buffer->Handle(), command_buffer, - offset_base, command_offset, command_stride, + scheduler.Record([draw_buffer_obj = draw_buffer->Handle(), + buffer_obj = buffer->Handle(), offset_base, offset, params](vk::CommandBuffer cmdbuf) { if (params.is_indexed) { - cmdbuf.DrawIndexedIndirectCount(command_buffer, command_offset, draw_buffer_obj, - offset_base, - static_cast(params.max_draw_counts), - command_stride); + cmdbuf.DrawIndexedIndirectCount( + buffer_obj, offset, draw_buffer_obj, offset_base, + static_cast(params.max_draw_counts), static_cast(params.stride)); } else { - cmdbuf.DrawIndirectCount(command_buffer, command_offset, draw_buffer_obj, - offset_base, + cmdbuf.DrawIndirectCount(buffer_obj, offset, draw_buffer_obj, offset_base, static_cast(params.max_draw_counts), - command_stride); + static_cast(params.stride)); } }); return; } - scheduler.Record([command_buffer, command_offset, command_stride, - params](vk::CommandBuffer cmdbuf) { + scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) { if (params.is_indexed) { - cmdbuf.DrawIndexedIndirect(command_buffer, command_offset, + cmdbuf.DrawIndexedIndirect(buffer_obj, offset, static_cast(params.max_draw_counts), - command_stride); + static_cast(params.stride)); } else { - cmdbuf.DrawIndirect(command_buffer, command_offset, - static_cast(params.max_draw_counts), command_stride); + cmdbuf.DrawIndirect(buffer_obj, offset, static_cast(params.max_draw_counts), + static_cast(params.stride)); } }); @@ -386,14 +361,14 @@ void RasterizerVulkan::DrawTexture() { texture_cache.SynchronizeDescriptors(false); texture_cache.UpdateRenderTargets(false); + UpdateDynamicStates(); + + query_cache.NotifySegment(true); + query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager.draw_texture_state; const auto& sampler = texture_cache.GetSampler(draw_texture_state.src_sampler, false); const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture); const auto* framebuffer = texture_cache.GetFramebuffer(); - scheduler.RequestRenderpass(framebuffer, 0, 0); - query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, - maxwell3d->regs.zpass_pixel_count_enable); const bool src_rescaling = texture_cache.IsRescaling() && texture.IsRescaled(); const bool dst_rescaling = texture_cache.IsRescaling() && framebuffer->IsRescaled(); @@ -418,9 +393,8 @@ void RasterizerVulkan::DrawTexture() { .y = ScaleSrc(draw_texture_state.src_y1)}}; Extent3D src_size = {static_cast(ScaleSrc(texture.size.width)), static_cast(ScaleSrc(texture.size.height)), texture.size.depth}; - blit_image.BlitColor(framebuffer, texture.Handle(Shader::TextureType::Color2D), - texture.ImageHandle(), sampler->Handle(), dst_region, src_region, - src_size); + blit_image.BlitColor(framebuffer, texture.RenderTarget(), texture.ImageHandle(), + sampler->Handle(), dst_region, src_region, src_size); } void RasterizerVulkan::Clear(u32 layer_count) { @@ -441,10 +415,15 @@ void RasterizerVulkan::Clear(u32 layer_count) { const Framebuffer* const framebuffer = texture_cache.GetFramebuffer(); const VkExtent2D render_area = framebuffer->RenderArea(); + constexpr bool ENABLE_DEFERRED_CLEAR = true; const bool color_full_channels = regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B && regs.clear_surface.A; const bool stencil_partial = use_stencil && framebuffer->HasAspectStencilBit() && regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0; + const bool ds_used = use_depth || use_stencil; + const bool ds_deferrable = + !ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) && + (!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial); u32 up_scale = 1; u32 down_shift = 0; if (texture_cache.IsRescaling()) { @@ -510,16 +489,16 @@ void RasterizerVulkan::Clear(u32 layer_count) { clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 && clear_rect.rect.extent.width >= render_area.width && clear_rect.rect.extent.height >= render_area.height; - const bool clear_on_load = (!regs.clear_control.use_scissor || clear_covers_render_area) && - regs.clear_surface.layer == 0 && - layer_count >= framebuffer->Attachments().layers && - !query_cache_runtime.IsHostConditionalRenderingSet(); - const auto request_renderpass = [&] { + const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) && + regs.clear_surface.layer == 0 && + !scheduler.IsRenderPassActive() && + (!use_color || color_full_channels) && ds_deferrable; + if (!can_defer_clear) { scheduler.RequestRenderpass(framebuffer); - query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, - maxwell3d->regs.zpass_pixel_count_enable); - }; + } + + query_cache.NotifySegment(true); + query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); UpdateViewportsState(regs); const u32 color_attachment = regs.clear_surface.RT; @@ -539,27 +518,26 @@ void RasterizerVulkan::Clear(u32 layer_count) { clear_value.color.int32[i] = s32(f32(s64(int_size - 1) << 1) * (regs.clear_color[i] - 0.5f)); } - if (!color_full_channels) { + if (color_full_channels) { + if (can_defer_clear) { + scheduler.DeferColorClear(framebuffer, color_attachment, clear_value); + } else { + scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { + const VkClearAttachment attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = color_attachment, + .clearValue = clear_value, + }; + cmdbuf.ClearAttachments(attachment, clear_rect); + }); + } + } else { u8 color_mask = u8(regs.clear_surface.R | regs.clear_surface.G << 1 | regs.clear_surface.B << 2 | regs.clear_surface.A << 3); Region2D dst_region = { Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, Offset2D{.x = clear_rect.rect.offset.x + s32(clear_rect.rect.extent.width), .y = clear_rect.rect.offset.y + s32(clear_rect.rect.extent.height)}}; - request_renderpass(); - blit_image.ClearColor(framebuffer, color_attachment, color_mask, regs.clear_color, - dst_region); - } else if (!clear_on_load || - !scheduler.OverrideLoadOps(framebuffer, 1u << color_attachment, - VK_ATTACHMENT_LOAD_OP_CLEAR, clear_value)) { - request_renderpass(); - scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { - const VkClearAttachment attachment{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .colorAttachment = color_attachment, - .clearValue = clear_value, - }; - cmdbuf.ClearAttachments(attachment, clear_rect); - }); + blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region); } } @@ -567,46 +545,41 @@ void RasterizerVulkan::Clear(u32 layer_count) { return; } VkImageAspectFlags aspect_flags = 0; - u32 ds_attachments = 0; if (use_depth && framebuffer->HasAspectDepthBit()) { aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT; - ds_attachments |= Scheduler::DEPTH_ATTACHMENT_BIT; } if (use_stencil && framebuffer->HasAspectStencilBit()) { aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT; - ds_attachments |= Scheduler::STENCIL_ATTACHMENT_BIT; } if (aspect_flags == 0) { return; } - if (stencil_partial) { + if (use_stencil && framebuffer->HasAspectStencilBit() && regs.stencil_front_mask != 0xFF && + regs.stencil_front_mask != 0) { Region2D dst_region = { Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, Offset2D{.x = clear_rect.rect.offset.x + s32(clear_rect.rect.extent.width), .y = clear_rect.rect.offset.y + s32(clear_rect.rect.extent.height)}}; - request_renderpass(); blit_image.ClearDepthStencil(framebuffer, use_depth, regs.clear_depth, u8(regs.stencil_front_mask), regs.clear_stencil, regs.stencil_front_func_mask, dst_region); - return; + } else if (can_defer_clear) { + VkClearValue ds_value{}; + ds_value.depthStencil.depth = regs.clear_depth; + ds_value.depthStencil.stencil = regs.clear_stencil; + scheduler.DeferDepthStencilClear(framebuffer, ds_value); + } else { + scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, + clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { + VkClearAttachment attachment; + attachment.aspectMask = aspect_flags; + attachment.colorAttachment = 0; + attachment.clearValue.depthStencil.depth = clear_depth; + attachment.clearValue.depthStencil.stencil = clear_stencil; + cmdbuf.ClearAttachments(attachment, clear_rect); + }); } - VkClearValue ds_value{}; - ds_value.depthStencil.depth = regs.clear_depth; - ds_value.depthStencil.stencil = regs.clear_stencil; - if (clear_on_load && scheduler.OverrideLoadOps(framebuffer, ds_attachments, - VK_ATTACHMENT_LOAD_OP_CLEAR, ds_value)) { - return; - } - request_renderpass(); - scheduler.Record([ds_value, clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { - const VkClearAttachment attachment{ - .aspectMask = aspect_flags, - .colorAttachment = 0, - .clearValue = ds_value, - }; - cmdbuf.ClearAttachments(attachment, clear_rect); - }); } void RasterizerVulkan::DispatchCompute() { @@ -623,14 +596,6 @@ void RasterizerVulkan::DispatchCompute() { return; } - const VkMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = device.GetBufferUserStages(), - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = vk::ACCESS_SHADER_RESOURCES, - }; const auto& qmd{kepler_compute->launch_description}; auto indirect_address = kepler_compute->GetIndirectComputeAddress(); if (indirect_address) { @@ -639,16 +604,12 @@ void RasterizerVulkan::DispatchCompute() { const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; const auto [buffer, offset] = buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); - scheduler.RequestComputeDispatchContext(); - VkMemoryBarrier2 indirect_read_barrier = read_barrier; - indirect_read_barrier.dstStageMask |= VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT; - indirect_read_barrier.dstAccessMask |= VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT; - scheduler.Record([pipeline, indirect_buffer = buffer->Handle(), indirect_offset = offset, - indirect_read_barrier](vk::CommandBuffer cmdbuf) { + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([pipeline, indirect_buffer = buffer->Handle(), + indirect_offset = offset](vk::CommandBuffer cmdbuf) { if (!pipeline->IsBound()) { return; } - cmdbuf.PipelineBarrier(indirect_read_barrier); cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); }); return; @@ -658,12 +619,19 @@ void RasterizerVulkan::DispatchCompute() { if (dim[0] > max_dim[0] || dim[1] > max_dim[1] || dim[2] > max_dim[2]) { return; } - scheduler.RequestComputeDispatchContext(); - scheduler.Record([pipeline, dim, read_barrier](vk::CommandBuffer cmdbuf) { + scheduler.RequestOutsideRenderPassOperationContext(); + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + }; + scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, READ_BARRIER); }); + scheduler.Record([pipeline, dim](vk::CommandBuffer cmdbuf) { if (!pipeline->IsBound()) { return; } - cmdbuf.PipelineBarrier(read_barrier); cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); @@ -830,6 +798,18 @@ void RasterizerVulkan::OnCacheInvalidation(DAddr addr, u64 size) { pipeline_cache.InvalidateRegion(addr, size); } +bool RasterizerVulkan::TryCacheInvalidation(DAddr addr, u64 size) { + if (addr == 0 || size == 0) return true; + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + if (buffer_cache.IsRegionGpuModified(addr, size)) return false; + texture_cache.WriteMemory(addr, size); + buffer_cache.WriteMemory(addr, size); + } + pipeline_cache.InvalidateRegion(addr, size); + return true; +} + void RasterizerVulkan::InvalidateGPUCache() { gpu.InvalidateGPUCache(); } @@ -862,8 +842,8 @@ void RasterizerVulkan::SyncOperation(std::function&& func) { fence_manager.SyncOperation(std::move(func)); } -void RasterizerVulkan::SignalSyncPoint(u32 value, u32 count) { - fence_manager.SignalSyncPoint(value, count); +void RasterizerVulkan::SignalSyncPoint(u32 value) { + fence_manager.SignalSyncPoint(value); } void RasterizerVulkan::SignalReference() { @@ -883,8 +863,25 @@ void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size, } void RasterizerVulkan::WaitForIdle() { + // Everything but wait pixel operations. This intentionally includes FRAGMENT_SHADER_BIT because + // fragment shaders can still write storage buffers. + VkPipelineStageFlags flags = + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | + VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; + if (device.IsExtTransformFeedbackSupported()) { + flags |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT; + } + query_cache.NotifyWFI(); - scheduler.NotifyWaitForIdle(); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([event = *wfi_event, flags](vk::CommandBuffer cmdbuf) { + cmdbuf.SetEvent(event, flags); + cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); + }); fence_manager.SignalOrdering(); } @@ -898,9 +895,7 @@ void RasterizerVulkan::TiledCacheBarrier() { } void RasterizerVulkan::FlushCommands() { - if (draw_tick != scheduler.CurrentTick() || - (scheduler.IsRenderPassActive() && - !maxwell3d->dirty.flags[VideoCommon::Dirty::RenderTargets])) { + if (draw_counter == 0) { return; } draw_counter = 0; @@ -999,21 +994,23 @@ void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_load void RasterizerVulkan::FlushWork() { #ifdef __ANDROID__ - static constexpr u32 DRAWS_TO_DISPATCH = 1024; + static constexpr u32 DRAWS_TO_DISPATCH = 512; + static constexpr u32 CHECK_MASK = 3; #else static constexpr u32 DRAWS_TO_DISPATCH = 4096; -#endif // __ANDROID__ static constexpr u32 CHECK_MASK = 7; +#endif // __ANDROID__ - if (++draw_counter >= DRAWS_TO_DISPATCH && - (!scheduler.IsRenderPassActive() || - maxwell3d->dirty.flags[VideoCommon::Dirty::RenderTargets])) { - scheduler.Flush(); - draw_counter = 0; - } else if ((draw_counter & CHECK_MASK) == CHECK_MASK && !scheduler.IsRenderPassActive()) { - scheduler.DispatchWork(); + static_assert(DRAWS_TO_DISPATCH % (CHECK_MASK + 1) == 0); + if ((++draw_counter & CHECK_MASK) != CHECK_MASK) { + return; } - draw_tick = scheduler.CurrentTick(); + if (draw_counter < DRAWS_TO_DISPATCH) { + scheduler.DispatchWork(); + return; + } + scheduler.Flush(); + draw_counter = 0; } AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_, @@ -1075,7 +1072,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, return DmaBufferImageCopy(copy_info, buffer_operand, image_operand); } -void RasterizerVulkan::UpdateDynamicStates(const GraphicsPipeline& pipeline) { +void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; auto& flags = maxwell3d->dirty.flags; const auto topology = maxwell3d->draw_manager.draw_state.topology; @@ -1123,13 +1120,27 @@ void RasterizerVulkan::UpdateDynamicStates(const GraphicsPipeline& pipeline) { } if (device.IsExtExtendedDynamicState3EnablesSupported()) { + using namespace Tegra::Engines; + // AMD Workaround: LogicOp incompatible with float render targets + if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || + device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { + const auto has_float = std::any_of( + regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), + [](const auto& attrib) { + return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float; + } + ); + if (regs.logic_op.enable) { + regs.logic_op.enable = static_cast(!has_float); + } + } UpdateLogicOpEnable(regs); UpdateDepthClampEnable(regs); UpdateLineRasterizationMode(regs); UpdateLineStippleEnable(regs); UpdateConservativeRasterizationMode(regs); - UpdateAlphaToCoverageEnable(regs, pipeline); - UpdateAlphaToOneEnable(regs, pipeline); + UpdateAlphaToCoverageEnable(regs); + UpdateAlphaToOneEnable(regs); } if (device.IsExtExtendedDynamicState3BlendingSupported()) { @@ -1138,8 +1149,10 @@ void RasterizerVulkan::UpdateDynamicStates(const GraphicsPipeline& pipeline) { UpdateColorWriteEnable(regs); } - if (pipeline.HasDynamicVertexInput()) { - UpdateVertexInput(regs, pipeline.VertexAttributeMask()); + if (device.IsExtVertexInputDynamicStateSupported()) { + if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) { + UpdateVertexInput(regs); + } } } @@ -1282,18 +1295,10 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs } void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) { - const bool zeta_changed = - std::exchange(depth_bias_zeta_format, regs.zeta.format) != regs.zeta.format; - const bool bias_changed = state_tracker.TouchDepthBias(); - if (!bias_changed && !zeta_changed) { + if (!state_tracker.TouchDepthBias()) { return; } float units = regs.depth_bias / 2.0f; - const bool is_float = regs.zeta.format == Tegra::DepthFormat::Z32_FLOAT || - regs.zeta.format == Tegra::DepthFormat::Z32_FLOAT_X24S8_UINT; - if (is_float && !device.IsExtDepthBiasControlSupported()) { - units *= 256.0f; - } const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::Z24_UNORM_S8_UINT || regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM || regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM || @@ -1673,33 +1678,36 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); } -void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, - const GraphicsPipeline& pipeline) { +void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchAlphaToCoverageEnable()) { + return; + } if (!device.SupportsDynamicState3AlphaToCoverageEnable()) { return; } - const bool enable = - pipeline.SupportsAlphaToCoverage() && regs.anti_alias_alpha_control.alpha_to_coverage != 0; - if (!state_tracker.TouchAlphaToCoverageEnable() && enable == alpha_to_coverage_enabled) { - return; - } - alpha_to_coverage_enabled = enable; + GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() && + regs.anti_alias_alpha_control.alpha_to_coverage != 0; scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE); }); } -void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, - const GraphicsPipeline& pipeline) { +void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchAlphaToOneEnable()) { + return; + } if (!device.SupportsDynamicState3AlphaToOneEnable()) { + static std::once_flag warn_alpha_to_one; + std::call_once(warn_alpha_to_one, [] { + LOG_WARNING(Render_Vulkan, + "Alpha-to-one is not supported on this device; forcing it disabled"); + }); return; } - const bool enable = - pipeline.SupportsAlphaToOne() && regs.anti_alias_alpha_control.alpha_to_one != 0; - if (!state_tracker.TouchAlphaToOneEnable() && enable == alpha_to_one_enabled) { - return; - } - alpha_to_one_enabled = enable; + GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() && + regs.anti_alias_alpha_control.alpha_to_one != 0; scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE); }); @@ -1773,17 +1781,7 @@ void RasterizerVulkan::UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs) { } void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { - u8 integer_targets = 0; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const auto format = regs.rt[index].format; - if (format != Tegra::RenderTargetFormat::NONE && - IsPixelFormatInteger(VideoCore::Surface::PixelFormatFromRenderTargetFormat(format))) { - integer_targets |= static_cast(1u << index); - } - } - const bool targets_changed = - std::exchange(blend_integer_targets, integer_targets) != integer_targets; - if (!state_tracker.TouchBlending() && !targets_changed) { + if (!state_tracker.TouchBlending()) { return; } @@ -1810,12 +1808,17 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { }); } - if (state_tracker.TouchBlendEnable() || targets_changed) { + if (state_tracker.TouchBlendEnable()) { std::array setup_enables{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const bool is_integer = ((integer_targets >> index) & 1) != 0; + bool is_integer = false; + if (regs.rt[index].format != Tegra::RenderTargetFormat::NONE) { + const auto format = + VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[index].format); + is_integer = IsPixelFormatInteger(format); + } setup_enables[index] = - static_cast(!is_integer && regs.blend.enable[index] != 0); + (!is_integer && regs.blend.enable[index] != 0) ? VK_TRUE : VK_FALSE; } scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { cmdbuf.SetColorBlendEnableEXT(0, setup_enables); @@ -1888,9 +1891,18 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& }); } -void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, - u32 attribute_mask) { +void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { auto& dirty{maxwell3d->dirty.flags}; + const bool vertex_input_dirty = dirty[Dirty::VertexInput]; + const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers]; + if (!vertex_input_dirty && !vertex_buffers_dirty) { + return; + } + dirty[Dirty::VertexInput] = false; + + boost::container::static_vector bindings; + boost::container::static_vector attributes; + const u32 max_attributes = static_cast(std::min(Maxwell::NumVertexAttributes, device.GetMaxVertexInputAttributes())); @@ -1898,25 +1910,11 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, static_cast(std::min(Maxwell::NumVertexArrays, device.GetMaxVertexInputBindings())); - bool changed = dirty[Dirty::VertexInput] || vertex_input_attributes != attribute_mask; - dirty[Dirty::VertexInput] = false; - vertex_input_attributes = attribute_mask; - for (u32 binding = 0; binding < max_bindings; ++binding) { - const u32 stride = regs.vertex_streams[binding].stride; - changed |= vertex_input_strides[binding] != stride; - vertex_input_strides[binding] = stride; - } - if (!changed) { - return; - } - - boost::container::static_vector bindings; - boost::container::static_vector attributes; for (u32 index = 0; index < max_attributes; ++index) { const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; const u32 binding{attribute.buffer}; - if (((attribute_mask >> index) & 1) == 0 || attribute.constant || binding >= max_bindings) { + if (attribute.constant || binding >= max_bindings) { continue; } attributes.push_back({ @@ -1931,20 +1929,14 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; - VkVertexInputRate input_rate = VK_VERTEX_INPUT_RATE_VERTEX; - u32 divisor = 1; - if (regs.vertex_stream_instances.IsInstancingEnabled(binding) && - input_binding.frequency != 0) { - input_rate = VK_VERTEX_INPUT_RATE_INSTANCE; - divisor = (std::min)(input_binding.frequency, device.GetMaxVertexAttribDivisor()); - } + const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, - .stride = vertex_input_strides[binding], - .inputRate = input_rate, - .divisor = divisor, + .stride = input_binding.stride, + .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, + .divisor = is_instanced ? input_binding.frequency : 1, }); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 1d54694f2c..ae9929ca0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -22,6 +22,7 @@ #include "video_core/renderer_vulkan/vk_fence_manager.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_query_cache.h" +#include "video_core/renderer_vulkan/vk_render_pass_cache.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" @@ -105,13 +106,14 @@ public: VideoCommon::CacheType which = VideoCommon::CacheType::All) override; void InnerInvalidation(std::span> sequences) override; void OnCacheInvalidation(DAddr addr, u64 size) override; + bool TryCacheInvalidation(DAddr addr, u64 size) override; bool OnCPUWrite(DAddr addr, u64 size) override; void InvalidateGPUCache() override; void UnmapMemory(DAddr addr, u64 size) override; void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; void SignalFence(std::function&& func) override; void SyncOperation(std::function&& func) override; - void SignalSyncPoint(u32 value, u32 count) override; + void SignalSyncPoint(u32 value) override; void SignalReference() override; void ReleaseFences(bool force = true) override; void FlushAndInvalidateRegion( @@ -154,11 +156,11 @@ private: static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); template - void PrepareDraw(bool is_indexed, bool skip_empty, Func&&); + void PrepareDraw(bool is_indexed, Func&&); void FlushWork(); - void UpdateDynamicStates(const GraphicsPipeline& pipeline); + void UpdateDynamicStates(); void HandleTransformFeedback(); @@ -179,14 +181,13 @@ private: void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs, - const GraphicsPipeline& pipeline); - void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs, - const GraphicsPipeline& pipeline); + void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); @@ -194,7 +195,7 @@ private: void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, u32 attribute_mask); + void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); Tegra::GPU& gpu; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -210,6 +211,7 @@ private: ComputePassDescriptorQueue compute_pass_descriptor_queue; DescriptorBufferRing descriptor_buffer_ring; BlitImageHelper blit_image; + RenderPassCache render_pass_cache; TextureCacheRuntime texture_cache_runtime; TextureCache texture_cache; @@ -221,19 +223,13 @@ private: AccelerateDMA accelerate_dma; FenceManager fence_manager; + vk::Event wfi_event; boost::container::static_vector image_view_indices; std::array image_view_ids; boost::container::static_vector sampler_handles; u32 draw_counter = 0; - u64 draw_tick = 0; - Tegra::DepthFormat depth_bias_zeta_format{}; - std::array vertex_input_strides{}; - u32 vertex_input_attributes{}; - u8 blend_integer_targets{}; - bool alpha_to_coverage_enabled{}; - bool alpha_to_one_enabled{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index b6be5317e0..4d8fa15d16 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -4,7 +4,9 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include "common/container/unordered_map.h" + +#include #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/vk_render_pass_cache.h" @@ -17,90 +19,107 @@ namespace { using VideoCore::Surface::PixelFormat; using VideoCore::Surface::SurfaceType; -struct AttachmentAspects { - bool depth; - bool stencil; -}; + constexpr SurfaceType GetSurfaceType(PixelFormat format) { + switch (format) { + // Depth formats + case PixelFormat::D16_UNORM: + case PixelFormat::D32_FLOAT: + case PixelFormat::X8_D24_UNORM: + return SurfaceType::Depth; -constexpr SurfaceType GetSurfaceType(PixelFormat format) { - switch (format) { - case PixelFormat::D16_UNORM: - case PixelFormat::D32_FLOAT: - case PixelFormat::X8_D24_UNORM: - return SurfaceType::Depth; - case PixelFormat::S8_UINT: - return SurfaceType::Stencil; - case PixelFormat::D24_UNORM_S8_UINT: - case PixelFormat::S8_UINT_D24_UNORM: - case PixelFormat::D32_FLOAT_S8_UINT: - return SurfaceType::DepthStencil; - default: - return SurfaceType::ColorTexture; - } -} + // Stencil formats + case PixelFormat::S8_UINT: + return SurfaceType::Stencil; -constexpr AttachmentAspects GetAttachmentAspects(PixelFormat format) { - const SurfaceType surface_type = GetSurfaceType(format); - return AttachmentAspects{ - .depth = surface_type == SurfaceType::Depth || surface_type == SurfaceType::DepthStencil, - .stencil = - surface_type == SurfaceType::Stencil || surface_type == SurfaceType::DepthStencil, - }; -} + // Depth+Stencil formats + case PixelFormat::D24_UNORM_S8_UINT: + case PixelFormat::S8_UINT_D24_UNORM: + case PixelFormat::D32_FLOAT_S8_UINT: + return SurfaceType::DepthStencil; -VkFormat AttachmentFormat(const Device& device, PixelFormat format) { - return MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, format).format; -} + // Everything else is a color texture + default: + return SurfaceType::ColorTexture; + } + } -VkRenderingAttachmentInfo MakeAttachment(VkImageView view) { - return { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .pNext = nullptr, - .imageView = view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - .resolveMode = VK_RESOLVE_MODE_NONE, - .resolveImageView = VK_NULL_HANDLE, - .resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .clearValue = {}, - }; -} -} // Anonymous namespace + VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat format, + VkSampleCountFlagBits samples, + VkAttachmentLoadOp load_op, + VkAttachmentStoreOp store_op) { + using MaxwellToVK::SurfaceFormat; -ResolveModes PickResolveModes(const Device& device, PixelFormat format) { - constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + const SurfaceType surface_type = GetSurfaceType(format); + const bool has_stencil = surface_type == SurfaceType::DepthStencil || + surface_type == SurfaceType::Stencil; - const AttachmentAspects aspects = GetAttachmentAspects(format); - const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0; - const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0; + return { + .flags = {}, + .format = SurfaceFormat(device, FormatType::Optimal, true, format).format, + .samples = samples, + .loadOp = load_op, + .storeOp = store_op, + .stencilLoadOp = has_stencil ? load_op : VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = has_stencil ? store_op : VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .finalLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + } - ResolveModes modes{ - .depth = VK_RESOLVE_MODE_NONE, - .stencil = VK_RESOLVE_MODE_NONE, - }; - if (aspects.depth && depth_mode_supported) { - modes.depth = mode; - } - if (aspects.stencil && stencil_mode_supported) { - modes.stencil = mode; - } - if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) { - return modes; - } - if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) { - modes.stencil = mode; - } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) { - modes.depth = mode; - } - return modes; -} + struct ResolveAspects { + bool depth; + bool stencil; + }; + + struct ResolveModes { + VkResolveModeFlagBits depth; + VkResolveModeFlagBits stencil; + }; + + constexpr ResolveAspects GetResolveAspects(PixelFormat format) { + const SurfaceType surface_type = GetSurfaceType(format); + return ResolveAspects{ + .depth = surface_type == SurfaceType::Depth || + surface_type == SurfaceType::DepthStencil, + .stencil = surface_type == SurfaceType::Stencil || + surface_type == SurfaceType::DepthStencil, + }; + } + + ResolveModes PickResolveModes(const Device& device, PixelFormat format) { + constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + + const ResolveAspects aspects = GetResolveAspects(format); + const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0; + const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0; + + ResolveModes modes{ + .depth = VK_RESOLVE_MODE_NONE, + .stencil = VK_RESOLVE_MODE_NONE, + }; + if (aspects.depth && depth_mode_supported) { + modes.depth = mode; + } + if (aspects.stencil && stencil_mode_supported) { + modes.stencil = mode; + } + if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) { + return modes; + } + if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) { + modes.stencil = mode; + } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) { + modes.depth = mode; + } + return modes; + } + } // Anonymous namespace bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) { if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) { return false; } - const AttachmentAspects aspects = GetAttachmentAspects(depth_format); + const ResolveAspects aspects = GetResolveAspects(depth_format); if (!aspects.depth && !aspects.stencil) { return false; } @@ -112,65 +131,214 @@ bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) return modes.depth == modes.stencil || device.SupportsIndependentResolveNone(); } -RenderingFormats MakeRenderingFormats(const Device& device, std::span colors, - PixelFormat depth) { - RenderingFormats formats{}; - for (size_t index = 0; index < colors.size(); ++index) { - if (colors[index] == PixelFormat::Invalid) { - continue; +RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} + +VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { + std::scoped_lock lock{mutex}; + const auto [pair, is_new] = cache.try_emplace(key); + if (!is_new) { + return *pair->second; + } + static constexpr size_t MAX_ATTACHMENTS = + 2 * std::tuple_size_v + 2; + boost::container::static_vector descriptions; + std::array references{}; + u32 num_attachments{}; + u32 num_colors{}; + for (size_t index = 0; index < key.color_formats.size(); ++index) { + const PixelFormat format{key.color_formats[index]}; + const bool is_valid{format != PixelFormat::Invalid}; + references[index] = VkAttachmentReference{ + .attachment = is_valid ? num_colors : VK_ATTACHMENT_UNUSED, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + if (is_valid) { + const VkAttachmentLoadOp load_op = (key.color_clear_mask & (1u << index)) != 0 + ? VK_ATTACHMENT_LOAD_OP_CLEAR + : VK_ATTACHMENT_LOAD_OP_LOAD; + const VkAttachmentStoreOp store_op = (key.color_discard_mask & (1u << index)) != 0 + ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE; + descriptions.push_back( + AttachmentDescription(*device, format, key.samples, load_op, store_op)); + num_attachments = static_cast(index + 1); + ++num_colors; } - formats.colors[index] = AttachmentFormat(device, colors[index]); - formats.num_colors = static_cast(index + 1); } - if (depth == PixelFormat::Invalid) { - return formats; + const bool has_depth{key.depth_format != PixelFormat::Invalid}; + VkAttachmentReference depth_reference{}; + if (key.depth_format != PixelFormat::Invalid) { + depth_reference = VkAttachmentReference{ + .attachment = num_colors, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear + ? VK_ATTACHMENT_LOAD_OP_CLEAR + : VK_ATTACHMENT_LOAD_OP_LOAD; + const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard + ? VK_ATTACHMENT_STORE_OP_DONT_CARE + : VK_ATTACHMENT_STORE_OP_STORE; + descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples, + depth_load_op, depth_store_op)); } - const VkFormat depth_format = AttachmentFormat(device, depth); - const AttachmentAspects aspects = GetAttachmentAspects(depth); - if (aspects.depth) { - formats.depth = depth_format; + std::array resolve_references{}; + const bool do_resolve_color = + key.resolve_color && key.samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0; + if (do_resolve_color) { + for (size_t index = 0; index < key.color_formats.size(); ++index) { + const PixelFormat format{key.color_formats[index]}; + const bool is_valid{format != PixelFormat::Invalid}; + resolve_references[index] = VkAttachmentReference{ + .attachment = is_valid ? static_cast(descriptions.size()) : VK_ATTACHMENT_UNUSED, + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + if (is_valid) { + VkAttachmentDescription resolve_desc = + AttachmentDescription(*device, format, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_STORE); + resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + descriptions.push_back(resolve_desc); + } + } } - if (aspects.stencil) { - formats.stencil = depth_format; + const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth && + key.samples != VK_SAMPLE_COUNT_1_BIT && + SupportsDepthStencilResolve(*device, key.depth_format); + VkAttachmentReference depth_resolve_reference{}; + if (do_resolve_depth_stencil) { + depth_resolve_reference = VkAttachmentReference{ + .attachment = static_cast(descriptions.size()), + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + VkAttachmentDescription resolve_desc = + AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + descriptions.push_back(resolve_desc); } - return formats; -} - -RenderingAttachments MakeRenderingAttachments(const RenderingFormats& formats, - std::span colors, - VkImageView depth, const VkRect2D& render_area, - u32 layers) { - RenderingAttachments attachments{ - .render_area = render_area, - .num_colors = formats.num_colors, - .layers = layers, + const VkSubpassDescription subpass{ + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = num_attachments, + .pColorAttachments = references.data(), + .pResolveAttachments = do_resolve_color ? resolve_references.data() : nullptr, + .pDepthStencilAttachment = has_depth ? &depth_reference : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + const VkSubpassDependency dependency{ + .srcSubpass = 0, // Current subpass + .dstSubpass = 0, // Same subpass (self-dependency) + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT }; - attachments.colors.fill(MakeAttachment(VK_NULL_HANDLE)); - std::ranges::transform(colors, attachments.colors.begin(), MakeAttachment); - attachments.depth = MakeAttachment(VK_NULL_HANDLE); - attachments.stencil = attachments.depth; - if (formats.depth != VK_FORMAT_UNDEFINED) { - attachments.depth.imageView = depth; - } - if (formats.stencil != VK_FORMAT_UNDEFINED) { - attachments.stencil.imageView = depth; - } - return attachments; -} -void BeginRendering(vk::CommandBuffer cmdbuf, const RenderingAttachments& attachments) { - cmdbuf.BeginRendering(VkRenderingInfo{ - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + if (device->IsKhrCreateRenderPass2Supported()) { + boost::container::static_vector descriptions2; + for (const VkAttachmentDescription& description : descriptions) { + descriptions2.push_back(VkAttachmentDescription2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .pNext = nullptr, + .flags = description.flags, + .format = description.format, + .samples = description.samples, + .loadOp = description.loadOp, + .storeOp = description.storeOp, + .stencilLoadOp = description.stencilLoadOp, + .stencilStoreOp = description.stencilStoreOp, + .initialLayout = description.initialLayout, + .finalLayout = description.finalLayout, + }); + } + const auto promote = [](const VkAttachmentReference& reference) { + return VkAttachmentReference2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + .pNext = nullptr, + .attachment = reference.attachment, + .layout = reference.layout, + .aspectMask = 0, + }; + }; + std::array references2{}; + std::array resolve_references2{}; + for (size_t index = 0; index < references.size(); ++index) { + references2[index] = promote(references[index]); + resolve_references2[index] = promote(resolve_references[index]); + } + const VkAttachmentReference2 depth_reference2 = promote(depth_reference); + const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference); + const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format); + const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + .pNext = nullptr, + .depthResolveMode = resolve_modes.depth, + .stencilResolveMode = resolve_modes.stencil, + .pDepthStencilResolveAttachment = &depth_resolve_reference2, + }; + const VkSubpassDescription2 subpass2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + .pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr, + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .viewMask = 0, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = num_attachments, + .pColorAttachments = references2.data(), + .pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr, + .pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + const VkSubpassDependency2 dependency2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + .pNext = nullptr, + .srcSubpass = dependency.srcSubpass, + .dstSubpass = dependency.dstSubpass, + .srcStageMask = dependency.srcStageMask, + .dstStageMask = dependency.dstStageMask, + .srcAccessMask = dependency.srcAccessMask, + .dstAccessMask = dependency.dstAccessMask, + .dependencyFlags = dependency.dependencyFlags, + .viewOffset = 0, + }; + pair->second = device->GetLogical().CreateRenderPass2({ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + .pNext = nullptr, + .flags = 0, + .attachmentCount = static_cast(descriptions2.size()), + .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(), + .subpassCount = 1, + .pSubpasses = &subpass2, + .dependencyCount = 1, + .pDependencies = &dependency2, + .correlatedViewMaskCount = 0, + .pCorrelatedViewMasks = nullptr, + }); + return *pair->second; + } + + pair->second = device->GetLogical().CreateRenderPass({ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, .flags = 0, - .renderArea = attachments.render_area, - .layerCount = attachments.layers, - .viewMask = 0, - .colorAttachmentCount = attachments.num_colors, - .pColorAttachments = attachments.colors.data(), - .pDepthAttachment = &attachments.depth, - .pStencilAttachment = &attachments.stencil, + .attachmentCount = static_cast(descriptions.size()), + .pAttachments = descriptions.empty() ? nullptr : descriptions.data(), + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 1, + .pDependencies = &dependency, }); + return *pair->second; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index a9c5b4e6a5..0edad4639e 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -6,67 +6,77 @@ #pragma once -#include -#include +#include +#include "common/container/unordered_map.h" +#include "common/container_hash.h" #include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { -class Device; +struct RenderPassKey { + bool operator==(const RenderPassKey&) const noexcept = default; -struct RenderingFormats { - constexpr auto operator<=>(const RenderingFormats&) const noexcept = default; + std::array color_formats; + VideoCore::Surface::PixelFormat depth_format; + VkSampleCountFlagBits samples; + bool resolve_color; + bool resolve_depth_stencil; + u32 color_clear_mask; + bool depth_stencil_clear; + u32 color_discard_mask; + bool depth_stencil_discard; +}; - [[nodiscard]] VkPipelineRenderingCreateInfo CreateInfo() const noexcept { - return { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, - .pNext = nullptr, - .viewMask = 0, - .colorAttachmentCount = num_colors, - .pColorAttachmentFormats = colors.data(), - .depthAttachmentFormat = depth, - .stencilAttachmentFormat = stencil, - }; +} // namespace Vulkan + +namespace std { +template <> +struct hash { + static_assert(std::tuple_size_v <= 8); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF); + static_assert(static_cast(VideoCore::Surface::PixelFormat::Max) <= 0xFF); + static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF); + + [[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept { + u64 formats = 0; + for (size_t index = 0; index < key.color_formats.size(); ++index) { + formats |= static_cast(key.color_formats[index]) << (index * 8); + } + const u64 state = static_cast(key.depth_format) | + (static_cast(key.samples) << 8) | + (static_cast(key.color_clear_mask) << 16) | + (static_cast(key.color_discard_mask) << 24) | + (static_cast(key.resolve_color) << 32) | + (static_cast(key.depth_stencil_clear) << 33) | + (static_cast(key.resolve_depth_stencil) << 34) | + (static_cast(key.depth_stencil_discard) << 35); + size_t seed = 0; + Common::HashCombine(seed, formats); + Common::HashCombine(seed, state); + return seed; } - - std::array colors{}; - u32 num_colors{}; - VkFormat depth{}; - VkFormat stencil{}; }; +} // namespace std -struct RenderingAttachments { - std::array colors; - VkRenderingAttachmentInfo depth; - VkRenderingAttachmentInfo stencil; - VkRect2D render_area; - u32 num_colors; - u32 layers; -}; +namespace Vulkan { -struct ResolveModes { - VkResolveModeFlagBits depth; - VkResolveModeFlagBits stencil; -}; +class Device; [[nodiscard]] bool SupportsDepthStencilResolve(const Device& device, VideoCore::Surface::PixelFormat depth_format); -[[nodiscard]] ResolveModes PickResolveModes(const Device& device, - VideoCore::Surface::PixelFormat format); +class RenderPassCache { +public: + explicit RenderPassCache(const Device& device_); -[[nodiscard]] RenderingFormats MakeRenderingFormats( - const Device& device, std::span colors, - VideoCore::Surface::PixelFormat depth); + VkRenderPass Get(const RenderPassKey& key); -[[nodiscard]] RenderingAttachments MakeRenderingAttachments(const RenderingFormats& formats, - std::span colors, - VkImageView depth, - const VkRect2D& render_area, - u32 layers); - -void BeginRendering(vk::CommandBuffer cmdbuf, const RenderingAttachments& attachments); +private: + const Device* device{}; + ::Common::unordered_map cache; + std::mutex mutex; +}; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 1851f0b628..4a7a7d5e78 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -4,10 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include -#include #include #include @@ -15,7 +13,6 @@ #include "video_core/renderer_vulkan/vk_query_cache.h" -#include "common/make_unique_for_overwrite.h" #include "common/settings.h" #include "common/thread.h" #include "video_core/gpu_logging/gpu_logging.h" @@ -29,35 +26,6 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { -namespace { -constexpr size_t DEPTH_VIEW = 8; - -struct BeginRenderingCommand { - void operator()(vk::CommandBuffer cmdbuf, vk::CommandBuffer) const { - BeginRendering(cmdbuf, attachments); - } - - RenderingAttachments attachments; -}; - -VkImageView DepthStencilView(const RenderingAttachments& attachments) { - if (attachments.depth.imageView != VK_NULL_HANDLE) { - return attachments.depth.imageView; - } - return attachments.stencil.imageView; -} - -VkAccessFlags2 AttachmentWriteAccess(VkImageAspectFlags aspect_mask) { - if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { - return VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; - } - if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { - return VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - } - return VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; -} -} // Anonymous namespace void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { @@ -78,25 +46,6 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) : device{device_}, state_tracker{state_tracker_}, master_semaphore{std::make_unique(device)}, command_pool{std::make_unique(*master_semaphore, device)} { - compute_write_barrier = VkMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = device.GetBufferConsumerStages() | vk::PIPELINE_STAGE_ATTACHMENTS, - .dstAccessMask = device.GetBufferConsumerAccess() | vk::ACCESS_ATTACHMENTS, - }; - renderpass_write_barrier = compute_write_barrier; - renderpass_write_barrier.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_SHADERS; - upload_write_barrier = compute_write_barrier; - upload_write_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - upload_write_barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; - if (device.IsExtTransformFeedbackSupported()) { - renderpass_write_barrier.srcStageMask |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT; - renderpass_write_barrier.srcAccessMask |= - VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | - VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; - } AcquireNewChunk(); AllocateWorkerCommandBuffer(); @@ -107,7 +56,9 @@ Scheduler::~Scheduler() = default; u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { // When flushing, we only send data to the worker thread; no waiting is necessary. - return SubmitExecution(signal_semaphore, wait_semaphore); + const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore); + AllocateNewContext(); + return signal_value; } void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { @@ -115,6 +66,7 @@ void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) const u64 presubmit_tick = CurrentTick(); SubmitExecution(signal_semaphore, wait_semaphore); Wait(presubmit_tick); + AllocateNewContext(); } void Scheduler::WaitWorker() { @@ -137,219 +89,138 @@ void Scheduler::DispatchWork() { work_queue.push(std::move(chunk)); } event_cv.notify_all(); - recorded_attachments = nullptr; - ended_attachments = nullptr; AcquireNewChunk(); } } -void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer) { - const RenderingAttachments& attachments = framebuffer->Attachments(); - PublishComputeWrites(); - state.framebuffer_id = framebuffer->Id(); - ++renderpass_serial; - renderpass_pristine = true; - alias_framebuffer_id = 0; - std::ranges::transform(attachments.colors, renderpass_views.begin(), - &VkRenderingAttachmentInfo::imageView); - renderpass_views[DEPTH_VIEW] = DepthStencilView(attachments); - renderpass_extent = attachments.render_area.extent; - renderpass_layers = attachments.layers; - attachments_touched = 0; - attachments_written = 0; +void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, + const VkClearValue* clear_values, u32 clear_value_count) { + const VkFramebuffer framebuffer_handle = framebuffer->Handle(); + const VkExtent2D render_area = framebuffer->RenderArea(); + state.renderpass = renderpass; + state.framebuffer = framebuffer_handle; + state.render_area = render_area; if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { - const VkExtent2D render_area = attachments.render_area.extent; const std::string render_pass_info = fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height, framebuffer->NumImages()); GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - BeginRenderingCommand command{attachments}; - BeginRenderingCommand* recorded = chunk->Record(command); - if (recorded == nullptr) { - DispatchWork(); - recorded = chunk->Record(command); + std::array values{}; + for (u32 i = 0; i < clear_value_count && i < values.size(); ++i) { + values[i] = clear_values[i]; } - recorded_attachments = &recorded->attachments; + Record([renderpass, framebuffer_handle, render_area, values, clear_value_count]( + vk::CommandBuffer cmdbuf) { + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = renderpass, + .framebuffer = framebuffer_handle, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = render_area, + }, + .clearValueCount = clear_value_count, + .pClearValues = clear_value_count != 0 ? values.data() : nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + }); num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); framebuffer->MarkResolveShadowsUpToDate(); } -void Scheduler::BindFramebuffer(const Framebuffer* framebuffer) { - if (framebuffer->Id() == state.framebuffer_id) { +void Scheduler::RealizeDeferredClear() { + if (deferred_clear.framebuffer == nullptr) { return; } - if (CanAliasRenderPass(framebuffer)) { - alias_framebuffer_id = framebuffer->Id(); - return; + const DeferredClear dc = deferred_clear; + deferred_clear = {}; + + std::array clear_values{}; + u32 count = 0; + const RenderPassKey& base = dc.framebuffer->RenderPassKeyBase(); + for (u32 slot = 0; slot < 8; ++slot) { + if (base.color_formats[slot] == VideoCore::Surface::PixelFormat::Invalid) { + continue; + } + clear_values[count++] = dc.color_values[slot]; } + if (base.depth_format != VideoCore::Surface::PixelFormat::Invalid) { + clear_values[count++] = dc.depth_stencil_value; + } + const u32 color_discard_mask = + dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u; + const bool depth_stencil_discard = + dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil(); + const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant( + dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard); EndRenderPass(); - BeginRenderPassImpl(framebuffer); + BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count); } -bool Scheduler::CanAliasRenderPass(const Framebuffer* framebuffer) const { - const u64 id = framebuffer->Id(); - if (id == alias_framebuffer_id) { - return true; - } - if (id == state.framebuffer_id || state.framebuffer_id == 0 || - !device.IsExtDynamicRenderingUnusedAttachmentsSupported()) { +bool Scheduler::DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, + const VkClearValue& value) { + if (IsRenderPassActive()) { return false; } - const RenderingAttachments& attachments = framebuffer->Attachments(); - if (attachments.layers != renderpass_layers || - attachments.render_area.extent.width != renderpass_extent.width || - attachments.render_area.extent.height != renderpass_extent.height) { - return false; + if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) { + RealizeDeferredClear(); + EndRenderPass(); } - for (u32 slot = 0; slot < attachments.num_colors; ++slot) { - if (attachments.colors[slot].imageView != renderpass_views[slot]) { - return false; - } - } - const VkImageView depth_stencil = DepthStencilView(attachments); - return depth_stencil == VK_NULL_HANDLE || depth_stencil == renderpass_views[DEPTH_VIEW]; -} - -bool Scheduler::IsRenderPassImage(VkImage image) const { - const auto images = std::span(renderpass_images).first(num_renderpass_images); - return std::ranges::find(images, image) != images.end(); -} - -void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u32 written) { - BindFramebuffer(framebuffer); - renderpass_pristine = false; - attachments_touched |= touched; - attachments_written |= written; -} - -bool Scheduler::OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments, - VkAttachmentLoadOp load_op, const VkClearValue& value) { - BindFramebuffer(framebuffer); - if (!renderpass_pristine || recorded_attachments == nullptr) { - return false; - } - const auto set_load_op = [&](VkRenderingAttachmentInfo& attachment) { - attachment.loadOp = load_op; - attachment.clearValue = value; - }; - for (u32 slot = 0; slot < recorded_attachments->num_colors; ++slot) { - if ((attachments & (1u << slot)) != 0) { - set_load_op(recorded_attachments->colors[slot]); - } - } - if ((attachments & DEPTH_ATTACHMENT_BIT) != 0) { - set_load_op(recorded_attachments->depth); - } - if ((attachments & STENCIL_ATTACHMENT_BIT) != 0) { - set_load_op(recorded_attachments->stencil); - } - attachments_touched |= attachments; - attachments_written |= attachments; + deferred_clear.framebuffer = framebuffer; + deferred_clear.color_clear_mask |= 1u << rt_slot; + deferred_clear.color_values[rt_slot] = value; return true; } -void Scheduler::DiscardResolvedAttachments(VkImageView resolve_view) { - if (ended_attachments == nullptr) { +bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value) { + if (IsRenderPassActive()) { + return false; + } + if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) { + RealizeDeferredClear(); + EndRenderPass(); + } + deferred_clear.framebuffer = framebuffer; + deferred_clear.depth_stencil = true; + deferred_clear.depth_stencil_value = value; + return true; +} + +void Scheduler::FlushDeferredClear() { + if (deferred_clear.framebuffer == nullptr) { return; } - const auto discard = [resolve_view](VkRenderingAttachmentInfo& attachment) { - if (attachment.resolveImageView == resolve_view) { - attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - } - }; - std::ranges::for_each(std::span(ended_attachments->colors).first(ended_attachments->num_colors), - discard); - discard(ended_attachments->depth); - discard(ended_attachments->stencil); + RealizeDeferredClear(); + EndRenderPass(); +} + +void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { + if (deferred_clear.framebuffer == framebuffer) { + RealizeDeferredClear(); + return; + } + const VkRenderPass renderpass = framebuffer->RenderPass(); + const VkFramebuffer framebuffer_handle = framebuffer->Handle(); + const VkExtent2D render_area = framebuffer->RenderArea(); + if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer && + render_area.width == state.render_area.width && + render_area.height == state.render_area.height) { + return; + } + // Ends any active pass and realizes a deferred clear + EndRenderPass(); + BeginRenderPassImpl(framebuffer, renderpass, nullptr, 0); } void Scheduler::RequestOutsideRenderPassOperationContext() { EndRenderPass(); - PublishComputeWrites(); -} - -void Scheduler::RequestComputeDispatchContext() { - EndRenderPass(); - compute_writes = true; -} - -void Scheduler::RelaxAttachmentOps(RenderingAttachments& attachments) const { - const bool load_op_none = device.IsLoadOpNoneSupported(); - const bool drop_unused = device.IsExtDynamicRenderingUnusedAttachmentsSupported(); - const auto relax = [&](VkRenderingAttachmentInfo& attachment, u32 bit) { - if (attachment.imageView == VK_NULL_HANDLE) { - return true; - } - if (attachment.resolveMode != VK_RESOLVE_MODE_NONE || - attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR || - attachment.storeOp != VK_ATTACHMENT_STORE_OP_STORE || - (attachments_written & bit) != 0) { - return false; - } - attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE; - return (attachments_touched & bit) == 0; - }; - for (u32 slot = 0; slot < attachments.num_colors; ++slot) { - VkRenderingAttachmentInfo& color = attachments.colors[slot]; - if (!relax(color, 1u << slot)) { - continue; - } - if (drop_unused) { - color.imageView = VK_NULL_HANDLE; - } else if (load_op_none) { - color.loadOp = VK_ATTACHMENT_LOAD_OP_NONE; - } - } - const bool depth_unused = relax(attachments.depth, DEPTH_ATTACHMENT_BIT); - const bool stencil_unused = relax(attachments.stencil, STENCIL_ATTACHMENT_BIT); - if (drop_unused && depth_unused && stencil_unused) { - attachments.depth.imageView = VK_NULL_HANDLE; - attachments.stencil.imageView = VK_NULL_HANDLE; - } -} - -u32 Scheduler::RefineImageAccesses(const RenderingAttachments& attachments, - std::array& accesses) const { - const auto stores = [](const VkRenderingAttachmentInfo& attachment) { - return attachment.imageView != VK_NULL_HANDLE && - attachment.storeOp != VK_ATTACHMENT_STORE_OP_NONE; - }; - u32 unused_images = 0; - u32 image = 0; - const auto refine = [&](bool used, bool stored) { - if (!used) { - unused_images |= 1u << image; - } - if (!stored) { - accesses[image] = 0; - } - ++image; - }; - for (u32 slot = 0; slot < attachments.num_colors; ++slot) { - if (renderpass_views[slot] != VK_NULL_HANDLE) { - const VkRenderingAttachmentInfo& color = attachments.colors[slot]; - refine(color.imageView != VK_NULL_HANDLE, stores(color)); - } - } - if (renderpass_views[DEPTH_VIEW] != VK_NULL_HANDLE) { - refine(DepthStencilView(attachments) != VK_NULL_HANDLE, - stores(attachments.depth) || stores(attachments.stencil)); - } - return unused_images; -} - -void Scheduler::PublishComputeWrites() { - if (!std::exchange(compute_writes, false)) { - return; - } - Record([barrier = &compute_write_barrier](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(*barrier); - }); } bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) { @@ -469,14 +340,19 @@ void Scheduler::AllocateWorkerCommandBuffer() { } u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { - master_semaphore->Refresh(); EndPendingOperations(); InvalidateState(); const u64 signal_value = master_semaphore->NextTick(); RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { - upload_cmdbuf.PipelineBarrier(upload_write_barrier); + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; + upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); upload_cmdbuf.End(); cmdbuf.End(); @@ -492,7 +368,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { GPU::Logging::GPULogger::GetInstance().LogVulkanCall( - "vkQueueSubmit2", "", VK_SUCCESS); + "vkQueueSubmit", "", VK_SUCCESS); } break; case VK_ERROR_DEVICE_LOST: @@ -508,6 +384,10 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se return signal_value; } +void Scheduler::AllocateNewContext() { + // Enable counters once again. These are disabled when a command buffer is finished. +} + void Scheduler::InvalidateState() { state.graphics_pipeline = nullptr; state.rescaling_defined = false; @@ -520,75 +400,92 @@ void Scheduler::EndPendingOperations() { EndRenderPass(); } -void Scheduler::EndRenderPass() { - if (state.framebuffer_id == 0) { - return; - } - std::array accesses{}; - for (u32 index = 0; index < num_renderpass_images; ++index) { - accesses[index] = AttachmentWriteAccess(renderpass_image_ranges[index].aspectMask); - } - u32 unused_images = 0; - if (recorded_attachments != nullptr) { - RelaxAttachmentOps(*recorded_attachments); - unused_images = RefineImageAccesses(*recorded_attachments, accesses); - } - ended_attachments = std::exchange(recorded_attachments, nullptr); +void Scheduler::EndRenderPass() + { + RealizeDeferredClear(); + if (!state.renderpass) { + return; + } - query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); - // Log render pass end - if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { - GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); - } + // Log render pass end + if (GPU::Logging::IsActive() && + Settings::values.gpu_log_vulkan_calls.GetValue()) { + GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd(); + } - query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); - query_cache->NotifySegment(false); + query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false); + query_cache->NotifySegment(false); - Record([num_images = num_renderpass_images, images = renderpass_images, - ranges = renderpass_image_ranges, accesses, unused_images, - write_barrier = &renderpass_write_barrier, - num_memory_barriers = static_cast(std::exchange(renderpass_writes, false))]( - vk::CommandBuffer cmdbuf) { - std::array barriers; - size_t num_barriers = 0; - for (size_t i = 0; i < num_images; ++i) { - if (((unused_images >> i) & 1) != 0) { - continue; + Record([num_images = num_renderpass_images, + images = renderpass_images, + ranges = renderpass_image_ranges, + has_transform_feedback = device.IsExtTransformFeedbackSupported()]( + vk::CommandBuffer cmdbuf) { + std::array barriers; + for (size_t i = 0; i < num_images; ++i) { + const VkImageSubresourceRange& range = ranges[i]; + const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; + const bool is_depth_stencil = (range.aspectMask + & (VK_IMAGE_ASPECT_DEPTH_BIT + | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; + + VkAccessFlags src_access = 0; + + if (is_color) + src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + else if (is_depth_stencil) + src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + else + src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + barriers[i] = VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT + | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT + | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = images[i], + .subresourceRange = range, + }; } - barriers[num_barriers++] = VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_ATTACHMENTS, - .srcAccessMask = accesses[i], - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = images[i], - .subresourceRange = ranges[i], - }; - } - cmdbuf.EndRendering(); - if (num_barriers + num_memory_barriers != 0) { - cmdbuf.PipelineBarrier(0, vk::Span(write_barrier, num_memory_barriers), {}, - vk::Span(barriers.data(), num_barriers)); - } - }); + cmdbuf.EndRenderPass(); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); + if (has_transform_feedback) { + static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, XFB_OUTPUT_BARRIER); + } + }); + + state.renderpass = VkRenderPass{}; + num_renderpass_images = 0; + } - state.framebuffer_id = 0; - alias_framebuffer_id = 0; - num_renderpass_images = 0; -} void Scheduler::AcquireNewChunk() { std::scoped_lock rl{reserve_mutex}; if (chunk_reserve.empty()) { // If we don't have anything reserved, we need to make a new chunk. - chunk = Common::make_unique_for_overwrite(); + chunk = std::make_unique(); } else { // Otherwise, we can just take from the reserve. chunk = std::move(chunk_reserve.back()); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index a0bdeedccc..d4bc8d0ec5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -36,16 +35,11 @@ class GraphicsPipeline; class StateTracker; struct QueryCacheParams; -struct RenderingAttachments; /// The scheduler abstracts command buffer and fence management with an interface that's able to do /// OpenGL-like operations on Vulkan command buffers. class Scheduler { public: - static constexpr u32 DEPTH_ATTACHMENT_BIT = 1u << 8; - static constexpr u32 STENCIL_ATTACHMENT_BIT = 1u << 9; - static constexpr u32 ALL_ATTACHMENTS = 0x3FF; - explicit Scheduler(const Device& device, StateTracker& state_tracker); ~Scheduler(); @@ -63,50 +57,24 @@ public: void DispatchWork(); /// Requests to begin a renderpass. - void RequestRenderpass(const Framebuffer* framebuffer, u32 touched = ALL_ATTACHMENTS, - u32 written = ALL_ATTACHMENTS); + void RequestRenderpass(const Framebuffer* framebuffer); - bool OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments, - VkAttachmentLoadOp load_op, const VkClearValue& value); + /// Defers a full-attachment color clear so it becomes the next render pass. + bool DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, const VkClearValue& value); - void DiscardResolvedAttachments(VkImageView resolve_view); + /// Defers a full depth/stencil clear so it becomes the next render pass. + bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value); - bool CanAliasRenderPass(const Framebuffer* framebuffer) const; - - bool IsRenderPassImage(VkImage image) const; + /// Realizes any pending deferred clear before its framebuffer can be moved or freed. + void FlushDeferredClear(); /// Requests the current execution context to be able to execute operations only allowed outside /// of a renderpass. void RequestOutsideRenderPassOperationContext(); - void RequestComputeDispatchContext(); - /// Returns true when a render pass is currently active in the scheduler state. bool IsRenderPassActive() const { - return state.framebuffer_id != 0; - } - - u64 ActiveRenderPassSerial() const noexcept { - if (state.framebuffer_id != 0) { - return renderpass_serial; - } - return (std::numeric_limits::max)(); - } - - u64 WaitForIdleSerial() const noexcept { - return wfi_serial; - } - - void NotifyWaitForIdle() noexcept { - ++wfi_serial; - } - - void MarkRenderPassWrites() noexcept { - renderpass_writes = true; - } - - bool HasDepthWrites() const noexcept { - return (attachments_written & DEPTH_ATTACHMENT_BIT) != 0; + return state.renderpass != VK_NULL_HANDLE; } /// Update the pipeline to the current execution context. @@ -161,12 +129,39 @@ public: return master_semaphore->IsFree(tick); } - /// Waits for the given tick to trigger on the GPU. - void Wait(u64 tick) { - if (tick >= master_semaphore->CurrentTick()) { - Flush(); + /// Waits for the given GPU tick, optionally pacing frames. + void Wait(u64 tick, double target_fps = 0.0) { + if (tick > 0) { + if (tick >= master_semaphore->CurrentTick()) { + Flush(); + } + master_semaphore->Wait(tick); + } + if (Settings::values.use_speed_limit.GetValue() && target_fps > 0.0) { + auto now = std::chrono::steady_clock::now(); + if (last_target_fps != target_fps) { + frame_interval = std::chrono::duration_cast(std::chrono::duration(1.0 / target_fps)); + max_frame_count = static_cast(0.1 * target_fps); + last_target_fps = target_fps; + frame_counter = 0; + start_time = now; + } + frame_counter++; + auto target_time = start_time + frame_interval * frame_counter; + if (target_time >= now) { + constexpr auto spin_tail = std::chrono::milliseconds(1); + auto sleep_time = target_time - now; + if (sleep_time > spin_tail * 2) { + std::this_thread::sleep_for(sleep_time - spin_tail); + } + while (std::chrono::steady_clock::now() < target_time) { + std::this_thread::yield(); + } + } else if (frame_counter > max_frame_count) { + frame_counter = 0; + start_time = now; + } } - master_semaphore->Wait(tick); } /// Returns the master timeline semaphore. @@ -209,10 +204,6 @@ private: command(cmdbuf, upload_cmdbuf); } - T& Get() noexcept { - return command; - } - private: T command; }; @@ -222,18 +213,16 @@ private: void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf); template - T* Record(T& command) { + bool Record(T& command) { using FuncType = TypedCommand; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { - return nullptr; + return false; } Command* const current_last = last; - FuncType* const recorded = - new (data.data() + command_offset) FuncType(std::move(command)); - last = recorded; + last = new (data.data() + command_offset) FuncType(std::move(command)); if (current_last) { current_last->SetNext(last); @@ -241,7 +230,7 @@ private: first = last; } command_offset += sizeof(FuncType); - return &recorded->Get(); + return true; } void MarkSubmit() { @@ -262,11 +251,13 @@ private: size_t command_offset = 0; bool submit = false; - alignas(std::max_align_t) std::array data; + alignas(std::max_align_t) std::array data{}; }; struct State { - u64 framebuffer_id = 0; + VkRenderPass renderpass{}; + VkFramebuffer framebuffer{}; + VkExtent2D render_area = {0, 0}; GraphicsPipeline* graphics_pipeline = nullptr; bool is_rescaling = false; bool rescaling_defined = false; @@ -275,9 +266,20 @@ private: bool descriptor_buffer_bound = false; }; - void BeginRenderPassImpl(const Framebuffer* framebuffer); + struct DeferredClear { + const Framebuffer* framebuffer = nullptr; + u32 color_clear_mask = 0; + std::array color_values{}; + bool depth_stencil = false; + VkClearValue depth_stencil_value{}; + }; - void BindFramebuffer(const Framebuffer* framebuffer); + /// Begins a render pass for the given framebuffer, optionally with clear values. + void BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass, + const VkClearValue* clear_values, u32 clear_value_count); + + /// If a deferred clear is pending. + void RealizeDeferredClear(); void WorkerThread(std::stop_token stop_token); @@ -285,17 +287,12 @@ private: u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); + void AllocateNewContext(); + void EndPendingOperations(); void EndRenderPass(); - void PublishComputeWrites(); - - void RelaxAttachmentOps(RenderingAttachments& attachments) const; - - u32 RefineImageAccesses(const RenderingAttachments& attachments, - std::array& accesses) const; - void AcquireNewChunk(); const Device& device; @@ -309,30 +306,16 @@ private: vk::CommandBuffer current_cmdbuf; vk::CommandBuffer current_upload_cmdbuf; + DeferredClear deferred_clear; + std::unique_ptr chunk; std::function on_submit; State state; - u64 renderpass_serial = 0; - u64 alias_framebuffer_id = 0; - u64 wfi_serial = 0; - bool renderpass_writes = false; - bool renderpass_pristine = false; - bool compute_writes = false; - u32 attachments_touched = 0; - u32 attachments_written = 0; - RenderingAttachments* recorded_attachments = nullptr; - RenderingAttachments* ended_attachments = nullptr; - VkMemoryBarrier2 renderpass_write_barrier{}; - VkMemoryBarrier2 compute_write_barrier{}; - VkMemoryBarrier2 upload_write_barrier{}; u32 num_renderpass_images = 0; std::array renderpass_images{}; std::array renderpass_image_ranges{}; - std::array renderpass_views{}; - VkExtent2D renderpass_extent{}; - u32 renderpass_layers = 0; std::queue> work_queue; std::vector> chunk_reserve; @@ -341,6 +324,12 @@ private: std::mutex queue_mutex; std::condition_variable_any event_cv; std::jthread worker_thread; + + std::chrono::steady_clock::duration frame_interval{}; + std::chrono::steady_clock::time_point start_time{}; + double last_target_fps{}; + u64 max_frame_count{}; + u64 frame_counter{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index e2bc1ad3a4..7b6571d33d 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -28,7 +28,16 @@ using namespace Common::Literals; // Maximum potential alignment of a Vulkan buffer constexpr VkDeviceSize MAX_ALIGNMENT = 256; +// Stream buffer size in bytes +// *NIX drivers are more sensitive to increased buffers for streaming. +// Windows ones however, can intake bigger buffers and generally do not OOM. +// - GTX 960 on Windows will not OOM with 256mib +// - GT 1030 on ^NIX will OOM with 256mib +#if defined(__FreeBSD__) constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; +#else +constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB; +#endif size_t GetStreamBufferSize(const Device& device) { if (!device.HasDebuggingToolAttached()) { @@ -111,14 +120,7 @@ void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) { } void StagingBufferPool::TickFrame() { - for (size_t step = 0; step < NUM_LEVELS; ++step) { - current_delete_level = (current_delete_level + 1) % NUM_LEVELS; - if (!device_local_cache[current_delete_level].entries.empty() || - !upload_cache[current_delete_level].entries.empty() || - !download_cache[current_delete_level].entries.empty()) { - break; - } - } + current_delete_level = (current_delete_level + 1) % NUM_LEVELS; ReleaseCache(MemoryUsage::DeviceLocal); ReleaseCache(MemoryUsage::Upload); @@ -126,12 +128,10 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - const size_t wanted_begin = Region(free_iterator) + 1; - const size_t wanted_end = (std::min)(Region(iterator + size) + 1, NUM_SYNCS); - if (AreRegionsActive(wanted_begin, wanted_end)) { - if (auto ref = OverflowStreamBuffer(size, wanted_begin, wanted_end)) { - return *ref; - } + if (AreRegionsActive(Region(free_iterator) + 1, + (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) { + // Avoid waiting for the previous usages to be free + return GetStagingBuffer(size, MemoryUsage::Upload); } const u64 current_tick = scheduler.CurrentTick(); std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), @@ -147,9 +147,8 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { free_iterator = size; if (AreRegionsActive(0, Region(size) + 1)) { - if (auto ref = OverflowStreamBuffer(size, 0, Region(size) + 1)) { - return *ref; - } + // Avoid waiting for the previous usages to be free + return GetStagingBuffer(size, MemoryUsage::Upload); } } const size_t offset = iterator; @@ -165,27 +164,6 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { }; } -u64 StagingBufferPool::MaxRegionTick(size_t region_begin, size_t region_end) const { - u64 tick = 0; - for (size_t region = region_begin; region < region_end; ++region) { - tick = (std::max)(tick, sync_ticks[region]); - } - return tick; -} - -std::optional StagingBufferPool::OverflowStreamBuffer(size_t size, - size_t region_begin, - size_t region_end) { - if (cache_bytes[static_cast(MemoryUsage::Upload)] < stream_buffer_size) { - return GetStagingBuffer(size, MemoryUsage::Upload); - } - if (auto ref = TryGetReservedBuffer(size, MemoryUsage::Upload, false)) { - return ref; - } - scheduler.Wait(MaxRegionTick(region_begin, region_end)); - return std::nullopt; -} - bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const { const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick(); return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, @@ -233,8 +211,7 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage .size = 1ULL << log2_size, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -255,7 +232,6 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage device.IsBufferDeviceAddressSupported() ? device.GetLogical().GetBufferDeviceAddress(*buffer) : VkDeviceAddress{}; - cache_bytes[static_cast(usage)] += size_t{1} << log2_size; StagingBuffer& entry = GetCache(usage)[log2_size].entries.emplace_back(StagingBuffer{ .buffer = std::move(buffer), .device_address = buffer_address, @@ -284,12 +260,12 @@ StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage } void StagingBufferPool::ReleaseCache(MemoryUsage usage) { - ReleaseLevel(usage, current_delete_level); + ReleaseLevel(GetCache(usage), current_delete_level); } -void StagingBufferPool::ReleaseLevel(MemoryUsage usage, size_t log2) { +void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { constexpr size_t deletions_per_tick = 16; - auto& staging = GetCache(usage)[log2]; + auto& staging = cache[log2]; auto& entries = staging.entries; const size_t old_size = entries.size(); @@ -303,7 +279,6 @@ void StagingBufferPool::ReleaseLevel(MemoryUsage usage, size_t log2) { entries.erase(std::remove_if(begin, end, is_deletable), end); const size_t new_size = entries.size(); - cache_bytes[static_cast(usage)] -= (old_size - new_size) << log2; staging.delete_index += deletions_per_tick; if (staging.delete_index >= new_size) { staging.delete_index = 0; diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index d684a1289b..6d967abb41 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -7,7 +7,6 @@ #pragma once #include -#include #include #include "common/common_types.h" @@ -89,11 +88,6 @@ private: bool AreRegionsActive(size_t region_begin, size_t region_end) const; - u64 MaxRegionTick(size_t region_begin, size_t region_end) const; - - std::optional OverflowStreamBuffer(size_t size, size_t region_begin, - size_t region_end); - StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false); std::optional TryGetReservedBuffer(size_t size, MemoryUsage usage, @@ -105,7 +99,7 @@ private: void ReleaseCache(MemoryUsage usage); - void ReleaseLevel(MemoryUsage usage, size_t log2); + void ReleaseLevel(StagingBuffersCache& cache, size_t log2); size_t Region(size_t iter) const noexcept { return iter / region_size; } @@ -125,8 +119,6 @@ private: size_t free_iterator = 0; std::array sync_ticks{}; - std::array cache_bytes{}; - StagingBuffersCache device_local_cache; StagingBuffersCache upload_cache; StagingBuffersCache download_cache; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 49b5dded33..0b132c97d6 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -63,6 +63,7 @@ Maxwell3D::DirtyState::Flags MakeInvalidationFlags() { BlendEnable, ConservativeRasterizationMode, LineStippleEnable, + LineStippleParams, }; Maxwell3D::DirtyState::Flags flags{}; for (const int flag : INVALIDATION_FLAGS) { @@ -227,6 +228,7 @@ void SetupDirtyVertexBindings(Maxwell3D::DirtyState::Tables& tables) { void SetupRasterModes(Maxwell3D::DirtyState::Tables &tables) { auto& table = tables[0]; + table[OFF(line_stipple_params)] = LineStippleParams; table[OFF(conservative_raster_enable)] = ConservativeRasterizationMode; table[OFF(line_anti_alias_enable)] = LineRasterizationMode; } diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index eebd25a81e..6b47ba4176 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -56,6 +56,7 @@ enum : u8 { ConservativeRasterizationMode, LineRasterizationMode, LineStippleEnable, + LineStippleParams, DepthBiasEnable, StateEnable, LogicOp, @@ -88,6 +89,14 @@ public: stencil_reset = true; } + void InvalidateViewports() { + (*flags)[Dirty::Viewports] = true; + } + + void InvalidateScissors() { + (*flags)[Dirty::Scissors] = true; + } + void InvalidateStateEnableFlag() { (*flags)[Dirty::StateEnable] = true; } @@ -211,6 +220,7 @@ public: bool TouchLineStippleEnable() { return Exchange(Dirty::LineStippleEnable, false); } + bool TouchLineStipple() { return Exchange(Dirty::LineStippleParams, false); } bool TouchDepthBiasEnable() { return Exchange(Dirty::DepthBiasEnable, false); } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 328bb28897..fea5398557 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -9,6 +9,10 @@ #include #include +#ifdef __ANDROID__ +#include +#endif + #include "common/logging.h" #include "common/settings.h" #include "common/settings_enums.h" @@ -175,7 +179,35 @@ bool Swapchain::AcquireNextImage() { break; } - scheduler.Wait(resource_ticks[image_index]); + const auto wait_with_frame_pacing = [this] { + switch (Settings::values.frame_pacing_mode.GetValue()) { + case Settings::FramePacingMode::Target_Auto: + scheduler.Wait(resource_ticks[image_index]); + break; + case Settings::FramePacingMode::Target_30: + scheduler.Wait(resource_ticks[image_index], 30.0); + break; + case Settings::FramePacingMode::Target_60: + scheduler.Wait(resource_ticks[image_index], 60.0); + break; + case Settings::FramePacingMode::Target_90: + scheduler.Wait(resource_ticks[image_index], 90.0); + break; + case Settings::FramePacingMode::Target_120: + scheduler.Wait(resource_ticks[image_index], 120.0); + break; + } + }; + +#ifdef __ANDROID__ + if (android_get_device_api_level() >= 30) { + scheduler.Wait(resource_ticks[image_index]); + } else { + wait_with_frame_pacing(); + } +#else + wait_with_frame_pacing(); +#endif resource_ticks[image_index] = scheduler.CurrentTick(); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 62559355a9..d926cc118a 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -47,6 +47,11 @@ public: return IsSubOptimal() || NeedsPresentModeUpdate(); } + /// Returns true when the swapchain is outdated. + bool IsOutDated() const { + return is_outdated; + } + /// Returns true when the swapchain is suboptimal. bool IsSubOptimal() const { return is_suboptimal; @@ -64,6 +69,10 @@ public: return image_index; } + std::size_t GetFrameIndex() const { + return frame_index; + } + VkImage GetImageIndex(std::size_t index) const { return images[index]; } @@ -103,6 +112,7 @@ public: private: void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities); void CreateSemaphores(); + void CreateImageViews(); void Destroy(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 55837ba8c0..583cb6edaf 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -155,75 +155,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (Settings::values.accelerate_astc.GetValue() != Settings::AstcDecodeMode::Gpu) { return false; } - return info.size.depth == 1; -} - -[[nodiscard]] VkFormat UnswizzleStorageFormat(u32 bytes_per_block) { - switch (bytes_per_block) { - case 1: - return VK_FORMAT_R8_UINT; - case 2: - return VK_FORMAT_R16_UINT; - case 4: - return VK_FORMAT_R32_UINT; - case 8: - return VK_FORMAT_R32G32_UINT; - case 16: - return VK_FORMAT_R32G32B32A32_UINT; - default: - return VK_FORMAT_UNDEFINED; - } -} - -[[nodiscard]] bool IsUnswizzleAcceleratedFormat(const Device& device, PixelFormat format) { - if (!device.IsStorageBuffer8BitAccessSupported() || - !device.IsStorageBuffer16BitAccessSupported()) { - return false; - } - if (DefaultBlockWidth(format) != 1 || DefaultBlockHeight(format) != 1) { - return false; - } - if (VideoCore::Surface::GetFormatType(format) != SurfaceType::ColorTexture) { - return false; - } - const VkFormat storage_format = UnswizzleStorageFormat(BytesPerBlock(format)); - if (storage_format == VK_FORMAT_UNDEFINED) { - return false; - } - return device.IsFormatSupported(storage_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, - FormatType::Optimal); -} - -[[nodiscard]] bool WillUseAcceleratedUnswizzle(const Device& device, const ImageInfo& info) { - const bool supported_type = info.type == ImageType::e2D || info.type == ImageType::e3D || - info.type == ImageType::Linear; - if (!supported_type || info.num_samples > 1) { - return false; - } - if (!device.IsKhrImageFormatListSupported()) { - return false; - } - return IsUnswizzleAcceleratedFormat(device, info.format); -} - -[[nodiscard]] std::array ShaderStorageViewFormats(PixelFormat format) { - if (DefaultBlockWidth(format) != 1 || DefaultBlockHeight(format) != 1) { - return {}; - } - switch (BytesPerBlock(format)) { - case 1: - return {VK_FORMAT_R8_UINT, VK_FORMAT_R8_SINT}; - case 2: - return {VK_FORMAT_R16_UINT, VK_FORMAT_R16_SINT}; - case 4: - return {VK_FORMAT_R32_UINT, VK_FORMAT_UNDEFINED}; - case 8: - return {VK_FORMAT_R32G32_UINT, VK_FORMAT_UNDEFINED}; - case 16: - return {VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_UNDEFINED}; - default: - return {}; - } + return Settings::values.astc_recompression.GetValue() == + Settings::AstcRecompression::Uncompressed && + info.size.depth == 1; } [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info, @@ -283,13 +217,11 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, const ImageInfo& info, std::span view_formats, - std::optional format_override = {}, - VkImageUsageFlags host_usage = 0) { + std::optional format_override = {}) { if (info.type == ImageType::Buffer) { return vk::Image{}; } VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info, format_override); - image_ci.usage |= host_usage; const VkImageFormatListCreateInfo image_format_list = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, .pNext = nullptr, @@ -317,12 +249,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { } [[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image, - VkFormat format, - VkImageViewType view_type) { - u32 layer_count = VK_REMAINING_ARRAY_LAYERS; - if (view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) { - layer_count = 1; - } + VkFormat format) { static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, @@ -333,7 +260,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .pNext = &storage_image_view_usage_create_info, .flags = 0, .image = image, - .viewType = view_type, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, .format = format, .components{ .r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -346,7 +273,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .baseMipLevel = level, .levelCount = 1, .baseArrayLayer = 0, - .layerCount = layer_count, + .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }); } @@ -505,11 +432,9 @@ void SanitizeDepthStencilSwizzle(std::array& swizzle, }; } -[[nodiscard]] VkImageCopy2 MakeImageCopy(const VideoCommon::ImageCopy& copy, - VkImageAspectFlags aspect_mask) noexcept { - return VkImageCopy2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2, - .pNext = nullptr, +[[nodiscard]] VkImageCopy MakeImageCopy(const VideoCommon::ImageCopy& copy, + VkImageAspectFlags aspect_mask) noexcept { + return VkImageCopy{ .srcSubresource = MakeImageSubresourceLayers(copy.src_subresource, aspect_mask), .srcOffset = MakeOffset3D(copy.src_offset), .dstSubresource = MakeImageSubresourceLayers(copy.dst_subresource, aspect_mask), @@ -518,12 +443,9 @@ void SanitizeDepthStencilSwizzle(std::array& swizzle, }; } -[[nodiscard]] VkBufferImageCopy2 MakeBufferImageCopy(const VideoCommon::ImageCopy& copy, - bool is_src, - VkImageAspectFlags aspect_mask) noexcept { - return VkBufferImageCopy2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .pNext = nullptr, +[[nodiscard]] VkBufferImageCopy MakeBufferImageCopy(const VideoCommon::ImageCopy& copy, bool is_src, + VkImageAspectFlags aspect_mask) noexcept { + return VkBufferImageCopy{ .bufferOffset = 0, .bufferRowLength = 0, .bufferImageHeight = 0, @@ -534,13 +456,25 @@ void SanitizeDepthStencilSwizzle(std::array& swizzle, }; } -[[nodiscard]] boost::container::small_vector TransformBufferImageCopies( +[[maybe_unused]] [[nodiscard]] boost::container::small_vector +TransformBufferCopies(std::span copies, size_t buffer_offset) { + boost::container::small_vector result(copies.size()); + std::ranges::transform( + copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) { + return VkBufferCopy{ + .srcOffset = static_cast(copy.src_offset + buffer_offset), + .dstOffset = static_cast(copy.dst_offset), + .size = static_cast(copy.size), + }; + }); + return result; +} + +[[nodiscard]] boost::container::small_vector TransformBufferImageCopies( std::span copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) { struct Maker { - VkBufferImageCopy2 operator()(const BufferImageCopy& copy) const { - return VkBufferImageCopy2{ - .sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2, - .pNext = nullptr, + VkBufferImageCopy operator()(const BufferImageCopy& copy) const { + return VkBufferImageCopy{ .bufferOffset = copy.buffer_offset + buffer_offset, .bufferRowLength = copy.buffer_row_length, .bufferImageHeight = copy.buffer_image_height, @@ -569,14 +503,14 @@ void SanitizeDepthStencilSwizzle(std::array& swizzle, VkImageAspectFlags aspect_mask; }; if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - boost::container::small_vector result(copies.size() * 2); + boost::container::small_vector result(copies.size() * 2); std::ranges::transform(copies, result.begin(), Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT}); std::ranges::transform(copies, result.begin() + copies.size(), Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT}); return result; } else { - boost::container::small_vector result(copies.size()); + boost::container::small_vector result(copies.size()); std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask}); return result; } @@ -683,7 +617,14 @@ struct RangedBarrierRange { }; void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, VkImageAspectFlags aspect_mask, bool is_initialized, - std::span copies) { + std::span copies) { + static constexpr VkAccessFlags WRITE_ACCESS_FLAGS = + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags READ_ACCESS_FLAGS = VK_ACCESS_SHADER_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + // Compute exact mip/layer range being written to RangedBarrierRange range; for (const auto& region : copies) { @@ -691,13 +632,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im } const VkImageSubresourceRange subresource_range = range.SubresourceRange(aspect_mask); - const VkImageMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = WRITE_ACCESS_FLAGS, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -706,13 +645,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im .subresourceRange = subresource_range, }; - const VkImageMemoryBarrier2 write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -721,18 +658,24 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im .subresourceRange = subresource_range, }; - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies); // TODO: Move this to another API - cmdbuf.PipelineBarrier(0, {}, {}, write_barrier); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, nullptr, nullptr, write_barrier); } -[[nodiscard]] VkImageBlit2 MakeImageBlit(const Region2D& dst_region, const Region2D& src_region, - const VkImageSubresourceLayers& dst_layers, - const VkImageSubresourceLayers& src_layers) { - return VkImageBlit2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr, +[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region, + const VkImageSubresourceLayers& dst_layers, + const VkImageSubresourceLayers& src_layers) { + return VkImageBlit{ .srcSubresource = src_layers, .srcOffsets = { @@ -769,13 +712,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im dst_region.end.y - dst_region.start.y == src_region.end.y - src_region.start.y; } -[[nodiscard]] VkImageResolve2 MakeImageResolve(const Region2D& dst_region, - const Region2D& src_region, - const VkImageSubresourceLayers& dst_layers, - const VkImageSubresourceLayers& src_layers) { - return VkImageResolve2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2, - .pNext = nullptr, +[[nodiscard]] VkImageResolve MakeImageResolve(const Region2D& dst_region, + const Region2D& src_region, + const VkImageSubresourceLayers& dst_layers, + const VkImageSubresourceLayers& src_layers) { + return VkImageResolve{ .srcSubresource = src_layers, .srcOffset = { @@ -878,12 +819,10 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .x = static_cast(up_scaling ? scaled_width : extent.width), .y = static_cast(up_scaling ? scaled_height : extent.height), }; - boost::container::small_vector regions; + boost::container::small_vector regions; regions.reserve(resources.levels); for (s32 level = 0; level < resources.levels; level++) { regions.push_back({ - .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr, .srcSubresource{ .aspectMask = aspect_mask, .mipLevel = static_cast(level), @@ -930,13 +869,11 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .layerCount = VK_REMAINING_ARRAY_LAYERS, }; const std::array read_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -944,13 +881,13 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .image = src_image, .subresourceRange = subresource_range, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, // Discard contents .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -960,13 +897,11 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const }, }; const std::array write_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -974,13 +909,11 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .image = src_image, .subresourceRange = subresource_range, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -989,35 +922,12 @@ void BlitScale(Scheduler& scheduler, VkImage src_image, VkImage dst_image, const .subresourceRange = subresource_range, }, }; - cmdbuf.PipelineBarrier(0, {}, {}, read_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, read_barriers); cmdbuf.BlitImage(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions, vk_filter); - cmdbuf.PipelineBarrier(0, {}, {}, write_barriers); - }); -} -void RecordInitialLayout(Scheduler& scheduler, VkImage image, VkImageAspectFlags aspect_mask) { - const VkImageMemoryBarrier2 barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_NONE, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = aspect_mask, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - scheduler.RecordWithUploadBuffer([barrier](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { - upload_cmdbuf.PipelineBarrier(barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, nullptr, nullptr, write_barriers); }); } } // Anonymous namespace @@ -1026,35 +936,16 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched MemoryAllocator& memory_allocator_, StagingBufferPool& staging_buffer_pool_, BlitImageHelper& blit_image_helper_, + RenderPassCache& render_pass_cache_, DescriptorPool& descriptor_pool, ComputePassDescriptorQueue& compute_pass_descriptor_queue) : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, - resolution{Settings::values.resolution_info} { + render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} { if (Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu) { astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue, memory_allocator); } - if (device.IsKhrImageFormatListSupported() && device.IsStorageBuffer8BitAccessSupported() && - device.IsStorageBuffer16BitAccessSupported()) { - bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, - compute_pass_descriptor_queue); - bl_unswizzle_3d_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, - compute_pass_descriptor_queue); - pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, - compute_pass_descriptor_queue); - } - for (size_t index = 0; index < VideoCore::Surface::MaxPixelFormat; index++) { - const auto format = static_cast(index); - if (!device.IsHostImageCopySupported() || DefaultBlockWidth(format) == 1) { - continue; - } - const VkFormat vk_format = - MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format).format; - host_copy_formats[index] = - (device.GetPhysical().GetFormatProperties3(vk_format).optimalTilingFeatures & - VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT) != 0; - } if (!device.IsKhrImageFormatListSupported()) { return; } @@ -1071,24 +962,20 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched view_formats[index_a].push_back(view_info.format); } } - auto& formats = view_formats[index_a]; - for (const VkFormat storage_format : ShaderStorageViewFormats(image_format)) { - if (storage_format == VK_FORMAT_UNDEFINED || - std::ranges::find(formats, storage_format) != formats.end()) { - continue; - } - formats.push_back(storage_format); - } } + if (Settings::values.gpu_unswizzle_enabled.GetValue()) { + bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, + staging_buffer_pool, compute_pass_descriptor_queue); + } } void TextureCacheRuntime::Finish() { scheduler.Finish(); } -StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) { - return staging_buffer_pool.Request(size, MemoryUsage::Upload); +StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) { + return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred); } StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { @@ -1120,12 +1007,11 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { } VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { - const size_t wanted_size = (std::max)(needed_size, size_t{1}); - const auto level = (8 * sizeof(size_t)) - std::countl_zero(wanted_size - 1ULL); + const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL); if (buffers[level]) { return *buffers[level]; } - const auto new_size = Common::NextPow2(wanted_size); + const auto new_size = Common::NextPow2(needed_size); static constexpr VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; @@ -1195,7 +1081,6 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .layerCount = layers, }, }); - RecordInitialLayout(scheduler, *shadow.image, aspect_mask); shadow.format = format; shadow.extent = extent; shadow.layers = layers; @@ -1239,9 +1124,7 @@ void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) { } void TextureCacheRuntime::BarrierFeedbackLoop() { - if (scheduler.HasDepthWrites()) { - scheduler.RequestOutsideRenderPassOperationContext(); - } + scheduler.RequestOutsideRenderPassOperationContext(); } void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, @@ -1249,8 +1132,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, if (ENABLE_MSAA_RESOLVE_CONSUME) { InvalidateResolveShadow(dst.Handle()); } - boost::container::small_vector vk_in_copies(copies.size()); - boost::container::small_vector vk_out_copies(copies.size()); + boost::container::small_vector vk_in_copies(copies.size()); + boost::container::small_vector vk_out_copies(copies.size()); const VkImageAspectFlags src_aspect_mask = src.AspectMask(); const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); @@ -1279,28 +1162,32 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, vk_in_copies, vk_out_copies](vk::CommandBuffer cmdbuf) { RangedBarrierRange dst_range; RangedBarrierRange src_range; - for (const VkBufferImageCopy2& copy : vk_in_copies) { + for (const VkBufferImageCopy& copy : vk_in_copies) { src_range.AddLayers(copy.imageSubresource); } - for (const VkBufferImageCopy2& copy : vk_out_copies) { + for (const VkBufferImageCopy& copy : vk_out_copies) { dst_range.AddLayers(copy.imageSubresource); } - static constexpr VkMemoryBarrier2 READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + static constexpr VkMemoryBarrier READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_BUFFER_USERS, - .srcAccessMask = vk::ACCESS_BUFFER_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = vk::ACCESS_TRANSFER, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + }; + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1310,13 +1197,11 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, }, }; const std::array middle_in_barrier{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = 0, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1326,13 +1211,13 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, }, }; const std::array middle_out_barrier{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1342,13 +1227,16 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, }, }; const std::array post_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1357,15 +1245,19 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), }, }; - cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, {}, {}, pre_barriers); cmdbuf.CopyImageToBuffer(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, copy_buffer, vk_in_copies); - cmdbuf.PipelineBarrier(0, {}, {}, middle_in_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, WRITE_BARRIER, nullptr, middle_in_barrier); - cmdbuf.PipelineBarrier(0, READ_BARRIER, {}, middle_out_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, READ_BARRIER, {}, middle_out_barrier); cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); - cmdbuf.PipelineBarrier(0, {}, {}, post_barriers); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, {}, {}, post_barriers); }); } @@ -1386,24 +1278,7 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst return; } ASSERT(src.format == dst.format); - const bool is_resolve = is_src_msaa && !is_dst_msaa; - if (ENABLE_MSAA_RESOLVE_CONSUME && is_resolve && HaveSameExtent(dst_region, src_region)) { - const VkImageResolve2 resolve = MakeImageResolve( - dst_region, src_region, MakeSubresourceLayers(&dst), MakeSubresourceLayers(&src)); - const VkImageCopy2 region{ - .sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2, - .pNext = nullptr, - .srcSubresource = resolve.srcSubresource, - .srcOffset = resolve.srcOffset, - .dstSubresource = resolve.dstSubresource, - .dstOffset = resolve.dstOffset, - .extent = resolve.extent, - }; - if (CopyResolveShadow(src.ImageHandle(), dst.ImageHandle(), region)) { - return; - } - } - if (is_resolve && + if (is_src_msaa && !is_dst_msaa && (aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0) { UNIMPLEMENTED_MSG("Stencil-only MSAA resolve is not supported"); @@ -1426,6 +1301,7 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst } }(); // Use shader-based depth/stencil blits if hardware doesn't support the format + // Note: MSAA resolves (MSAA->single) use vkCmdResolveImage which works fine if (!can_blit_depth_stencil) { UNIMPLEMENTED_IF(is_src_msaa || is_dst_msaa); blit_image_helper.BlitDepthStencil(dst_framebuffer, src, dst_region, src_region, @@ -1446,6 +1322,7 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst return; } + const bool is_resolve = is_src_msaa && !is_dst_msaa; if (is_resolve && !HaveSameExtent(dst_region, src_region)) { blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region); return; @@ -1459,13 +1336,13 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst scheduler.Record([filter, dst_region, src_region, dst_image, src_image, dst_layers, src_layers, aspect_mask, is_resolve](vk::CommandBuffer cmdbuf) { const std::array read_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1479,13 +1356,13 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1500,13 +1377,14 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst }, }, }; - VkImageMemoryBarrier2 write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1520,7 +1398,8 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(0, {}, {}, read_barriers); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, read_barriers); if (is_resolve) { cmdbuf.ResolveImage(src_image, VK_IMAGE_LAYOUT_GENERAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -1532,11 +1411,16 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst src_image, VK_IMAGE_LAYOUT_GENERAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeImageBlit(dst_region, src_region, dst_layers, src_layers), vk_filter); } - cmdbuf.PipelineBarrier(write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, write_barrier); }); } void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) { + if (!dst->RenderPass()) { + return; + } + switch (dst_view.format) { case PixelFormat::R16_UNORM: if (src_view.format == PixelFormat::D16_UNORM) { @@ -1674,7 +1558,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, }; return ReinterpretImage(dst, src, std::span{&oneCopy, 1}); } - boost::container::small_vector vk_copies(copies.size()); + boost::container::small_vector vk_copies(copies.size()); const VkImageAspectFlags aspect_mask = dst.AspectMask(); ASSERT(aspect_mask == src.AspectMask()); @@ -1687,18 +1571,18 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, scheduler.Record([dst_image, src_image, aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { RangedBarrierRange dst_range; RangedBarrierRange src_range; - for (const VkImageCopy2& copy : vk_copies) { + for (const VkImageCopy& copy : vk_copies) { dst_range.AddLayers(copy.dstSubresource); src_range.AddLayers(copy.srcSubresource); } const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1706,13 +1590,13 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .image = src_image, .subresourceRange = src_range.SubresourceRange(aspect_mask), }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1722,13 +1606,11 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, }, }; const std::array post_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = 0, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1736,13 +1618,16 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .image = src_image, .subresourceRange = src_range.SubresourceRange(aspect_mask), }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1751,133 +1636,138 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .subresourceRange = dst_range.SubresourceRange(aspect_mask), }, }; - cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, pre_barriers); cmdbuf.CopyImage(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VideoCommon::FixSmallVectorADL(vk_copies)); - cmdbuf.PipelineBarrier(0, {}, {}, post_barriers); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk_copies); + cmdbuf.PipelineBarrier( + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, nullptr, nullptr, post_barriers); }); } -bool TextureCacheRuntime::CopyResolveShadow(VkImage msaa_image, VkImage dst_image, - const VkImageCopy2& region) { - const ResolveShadow* const shadow = GetValidResolveShadow(msaa_image); - if (shadow == nullptr || shadow->aspect_mask != region.srcSubresource.aspectMask || - region.srcSubresource.mipLevel != 0 || region.srcOffset.x < 0 || region.srcOffset.y < 0 || - static_cast(region.srcOffset.x) + region.extent.width > shadow->extent.width || - static_cast(region.srcOffset.y) + region.extent.height > shadow->extent.height || - region.srcSubresource.baseArrayLayer + region.srcSubresource.layerCount > - shadow->layers) { - return false; - } - const VkImageAspectFlags aspect_mask = shadow->aspect_mask; - const VkImage shadow_image = *shadow->image; - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([shadow_image, dst_image, region, aspect_mask](vk::CommandBuffer cmdbuf) { - const std::array pre_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = shadow_image, - .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, - VK_REMAINING_ARRAY_LAYERS}, - }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst_image, - .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, - VK_REMAINING_ARRAY_LAYERS}, - }, - }; - const std::array post_barriers{ - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = shadow_image, - .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, - VK_REMAINING_ARRAY_LAYERS}, - }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = dst_image, - .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, - VK_REMAINING_ARRAY_LAYERS}, - }, - }; - cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers); - cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); - cmdbuf.PipelineBarrier(0, {}, {}, post_barriers); - }); - const bool is_color = aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT; - if ((is_color && ENABLE_MSAA_COLOR_DISCARD) || - (!is_color && ENABLE_MSAA_DEPTH_STENCIL_DISCARD)) { - scheduler.DiscardResolvedAttachments(*shadow->view); - } - return true; -} - void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, std::span copies) { const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1; const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples; if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 && - src.info.format == dst.info.format && copies.front().src_offset.x == 0 && - copies.front().src_offset.y == 0) { + src.info.format == dst.info.format) { const VideoCommon::ImageCopy& copy = copies.front(); - const VkImageCopy2 region{ - .sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2, - .pNext = nullptr, - .srcSubresource{ - .aspectMask = dst.AspectMask(), - .mipLevel = static_cast(copy.src_subresource.base_level), - .baseArrayLayer = static_cast(copy.src_subresource.base_layer), - .layerCount = static_cast(copy.src_subresource.num_layers), - }, - .srcOffset = {0, 0, 0}, - .dstSubresource{ - .aspectMask = dst.AspectMask(), - .mipLevel = static_cast(copy.dst_subresource.base_level), - .baseArrayLayer = static_cast(copy.dst_subresource.base_layer), - .layerCount = static_cast(copy.dst_subresource.num_layers), - }, - .dstOffset = {copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z}, - .extent = {copy.extent.width, copy.extent.height, 1}, - }; - if (CopyResolveShadow(src.Handle(), dst.Handle(), region)) { + const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle()); + if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() && + copy.src_offset.x == 0 && copy.src_offset.y == 0 && + copy.src_subresource.base_level == 0 && + static_cast(copy.extent.width) <= shadow->extent.width && + static_cast(copy.extent.height) <= shadow->extent.height && + static_cast(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <= + shadow->layers) { + const VkImageAspectFlags aspect_mask = shadow->aspect_mask; + VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkAccessFlags attachment_read_write = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + const VkImage shadow_image = *shadow->image; + const VkImage dst_image = dst.Handle(); + const VkImageCopy region{ + .srcSubresource{ + .aspectMask = aspect_mask, + .mipLevel = 0, + .baseArrayLayer = static_cast(copy.src_subresource.base_layer), + .layerCount = static_cast(copy.src_subresource.num_layers), + }, + .srcOffset = {0, 0, 0}, + .dstSubresource{ + .aspectMask = aspect_mask, + .mipLevel = static_cast(copy.dst_subresource.base_level), + .baseArrayLayer = static_cast(copy.dst_subresource.base_layer), + .layerCount = static_cast(copy.dst_subresource.num_layers), + }, + .dstOffset = {copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z}, + .extent = {copy.extent.width, copy.extent.height, 1}, + }; + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage, + attachment_write, + attachment_read_write](vk::CommandBuffer cmdbuf) { + const std::array pre_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = attachment_write, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = shadow_image, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write | + VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}, + }, + }; + const std::array post_barriers{ + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = shadow_image, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}, + }, + VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = dst_image, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}, + }, + }; + cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr, + pre_barriers); + cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, nullptr, + post_barriers); + }); return; } } @@ -1885,17 +1775,17 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, if ((dst_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { const bool copies_stencil = (dst_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && device.IsExtShaderStencilExportSupported(); - blit_image_helper.CopyMSAADepth(dst.Handle(), dst.info.format, src.Handle(), - src.info.format, num_samples, copies, copies_stencil, - msaa_to_non_msaa); + blit_image_helper.CopyMSAADepth(render_pass_cache, dst.Handle(), dst.info.format, + src.Handle(), src.info.format, num_samples, copies, + copies_stencil, msaa_to_non_msaa); return; } if ((dst_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); return; } - blit_image_helper.CopyMSAA(dst.Handle(), dst.info.format, src.Handle(), src.info.format, - num_samples, copies, msaa_to_non_msaa); + blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(), + src.info.format, num_samples, copies, msaa_to_non_msaa); } u64 TextureCacheRuntime::GetDeviceLocalMemory() const { @@ -1924,6 +1814,10 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +void TextureCacheRuntime::FlushDeferredClear() { + scheduler.FlushDeferredClear(); +} + VkImage TextureCacheRuntime::AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci) { const MsaaScratchKey key{ .format = image_ci.format, @@ -1981,15 +1875,17 @@ void TextureCacheRuntime::TickFrame() { Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, - runtime{&runtime_}, aspect_mask(ImageAspectMask(info.format)) { + runtime{&runtime_}, + original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, + runtime->ViewFormats(info.format))), + aspect_mask(ImageAspectMask(info.format)) { if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { switch (Settings::values.accelerate_astc.GetValue()) { case Settings::AstcDecodeMode::Gpu: if (WillUseAcceleratedAstcDecode(runtime->device, info)) { flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; - break; } - [[fallthrough]]; + break; case Settings::AstcDecodeMode::CpuAsynchronous: flags |= VideoCommon::ImageFlagBits::AsynchronousDecode; break; @@ -2003,24 +1899,18 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::CostlyLoad; } - if (False(flags & VideoCommon::ImageFlagBits::Converted) && - WillUseAcceleratedUnswizzle(runtime->device, info)) { - flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; - } - VkImageUsageFlags host_usage{}; - if (False(flags & VideoCommon::ImageFlagBits::Converted) && - runtime->host_copy_formats[static_cast(info.format)]) { - host_usage = VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT; - } - original_image = MakeImage(runtime->device, runtime->memory_allocator, info, - runtime->ViewFormats(info.format), {}, host_usage); if (runtime->device.HasDebuggingToolAttached()) { original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); } current_image = &Image::original_image; storage_image_views.resize(info.resources.levels); - if (original_image) { - runtime->TransitionImageLayout(*this); + if (WillUseAcceleratedAstcDecode(runtime->device, info)) { + const auto& device = runtime->device.GetLogical(); + const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + for (s32 level = 0; level < info.resources.levels; ++level) { + storage_image_views[level] = + MakeStorageView(device, level, *original_image, storage_format); + } } } @@ -2037,6 +1927,48 @@ Image::~Image() { } } +void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) { + using VideoCore::Surface::BytesPerBlock; + + const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H + const u32 block_width = 4; + const u32 block_height = 4; + + // BCn is 4x4x1 blocks + const u32 blocks_x = (info.size.width + block_width - 1) / block_width; + const u32 blocks_y = (info.size.height + block_height - 1) / block_height; + const u32 blocks_z = (std::min)(max_slices, info.size.depth); + + const u64 block_count = + static_cast(blocks_x) * + static_cast(blocks_y) * + static_cast(blocks_z); + + const VkDeviceSize required_size = block_count * block_bytes; + if (has_compute_unswizzle_buffer && required_size <= compute_unswizzle_buffer_size) { + return; + } + + compute_unswizzle_buffer_size = required_size; + + VkBufferCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = compute_unswizzle_buffer_size, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + compute_unswizzle_buffer = + runtime->memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal); + + has_compute_unswizzle_buffer = true; +} + void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, std::span copies) { // TODO: Move this to another API @@ -2074,7 +2006,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies)); + CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies); }); const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); @@ -2093,13 +2025,14 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, } if (msaa_upload_is_depth) { - runtime->blit_image_helper.CopyMSAADepth(Handle(), info.format, temp_vk_image, - info.format, info.num_samples, + runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(), + info.format, temp_vk_image, info.format, + info.num_samples, {image_copies.data(), image_copies.size()}, msaa_upload_copies_stencil, false); } else { - runtime->blit_image_helper.CopyMSAA(Handle(), info.format, temp_vk_image, info.format, - info.num_samples, + runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format, + temp_vk_image, info.format, info.num_samples, {image_copies.data(), image_copies.size()}, false); } initialized = true; @@ -2118,25 +2051,17 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, return; } + scheduler->RequestOutsideRenderPassOperationContext(); auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); const VkBuffer src_buffer = buffer; const VkImage vk_image = *original_image; const VkImageAspectFlags vk_aspect_mask = aspect_mask; const bool was_initialized = std::exchange(initialized, true); - auto copy = [src_buffer, vk_image, vk_aspect_mask, was_initialized, - vk_copies](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, VideoCommon::FixSmallVectorADL(vk_copies)); - }; - if (True(flags & ImageFlagBits::ReorderableUpload)) { - scheduler->RecordWithUploadBuffer( - [copy = std::move(copy)](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { - copy(upload_cmdbuf); - }); - } else { - scheduler->RequestOutsideRenderPassOperationContext(); - scheduler->Record(std::move(copy)); - } + scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized, + vk_copies](vk::CommandBuffer cmdbuf) { + CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, vk_copies); + }); if (is_rescaled) { ScaleUp(); @@ -2147,33 +2072,6 @@ void Image::UploadMemory(const StagingBufferRef& map, std::span memory, - std::span copies) { - const auto vk_copies = TransformBufferImageCopies(copies, 0, aspect_mask); - boost::container::small_vector regions; - for (const VkBufferImageCopy2& copy : vk_copies) { - regions.push_back(VkMemoryToImageCopyEXT{ - .sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT, - .pNext = nullptr, - .pHostPointer = memory.data() + copy.bufferOffset, - .memoryRowLength = copy.bufferRowLength, - .memoryImageHeight = copy.bufferImageHeight, - .imageSubresource = copy.imageSubresource, - .imageOffset = copy.imageOffset, - .imageExtent = copy.imageExtent, - }); - } - runtime->device.GetLogical().CopyMemoryToImageEXT(VkCopyMemoryToImageInfoEXT{ - .sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .dstImage = *original_image, - .dstImageLayout = VK_IMAGE_LAYOUT_GENERAL, - .regionCount = static_cast(regions.size()), - .pRegions = regions.data(), - }); -} - void Image::DownloadMemory(VkBuffer buffer, size_t offset, std::span copies) { std::array buffer_handles{ @@ -2210,25 +2108,23 @@ void Image::DownloadMemory(std::span buffers_span, std::span o const VkImage temp_vk_image = runtime->AcquireMsaaScratchImage(image_ci); const VkImageAspectFlags temp_aspect_mask = aspect_mask; - const VkAccessFlags2 attachment_access = - msaa_download_is_depth ? (VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) - : (VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT); - const VkPipelineStageFlags2 attachment_stage = - msaa_download_is_depth ? (VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT) - : VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + const VkAccessFlags attachment_access = + msaa_download_is_depth ? (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) + : (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + const VkPipelineStageFlags attachment_stage = + msaa_download_is_depth ? (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT) + : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; scheduler->RequestOutsideRenderPassOperationContext(); scheduler->Record([temp_vk_image, temp_aspect_mask, attachment_access, attachment_stage](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 init_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier init_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, .srcAccessMask = 0, - .dstStageMask = attachment_stage, .dstAccessMask = attachment_access, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -2243,7 +2139,8 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(init_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, attachment_stage, 0, + init_barrier); }); boost::container::small_vector image_copies; @@ -2260,18 +2157,19 @@ void Image::DownloadMemory(std::span buffers_span, std::span o if (msaa_download_is_depth) { runtime->blit_image_helper.CopyMSAADepth( - temp_vk_image, info.format, Handle(), info.format, info.num_samples, - {image_copies.data(), image_copies.size()}, msaa_download_copies_stencil, true); + runtime->render_pass_cache, temp_vk_image, info.format, Handle(), info.format, + info.num_samples, {image_copies.data(), image_copies.size()}, + msaa_download_copies_stencil, true); } else { - runtime->blit_image_helper.CopyMSAA(temp_vk_image, info.format, Handle(), - info.format, info.num_samples, + runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, temp_vk_image, + info.format, Handle(), info.format, + info.num_samples, {image_copies.data(), image_copies.size()}, true); } boost::container::small_vector buffers_vector{}; - boost::container::small_vector< - boost::container::small_vector, 8> + boost::container::small_vector, 8> vk_copies; for (size_t index = 0; index < buffers_span.size(); index++) { buffers_vector.emplace_back(buffers_span[index]); @@ -2281,17 +2179,12 @@ void Image::DownloadMemory(std::span buffers_span, std::span o scheduler->RequestOutsideRenderPassOperationContext(); scheduler->Record([buffers = std::move(buffers_vector), image = temp_vk_image, - aspect_mask_ = aspect_mask, vk_copies, - consumer_stages = runtime->device.GetBufferConsumerStages(), - consumer_access = runtime->device.GetBufferConsumerAccess()]( - vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -2305,28 +2198,25 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, read_barrier); for (size_t index = 0; index < buffers.size(); index++) { cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers[index], vk_copies[index]); } - const VkMemoryBarrier2 memory_write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + const VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = consumer_stages, - .dstAccessMask = consumer_access, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - const VkImageMemoryBarrier2 image_write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -2340,7 +2230,8 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(0, memory_write_barrier, {}, image_write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, memory_write_barrier, nullptr, image_write_barrier); }); runtime->ReleaseMsaaScratchImage(temp_vk_image); } @@ -2350,7 +2241,7 @@ void Image::DownloadMemory(std::span buffers_span, std::span o return; } else { boost::container::small_vector buffers_vector{}; - boost::container::small_vector, 8> + boost::container::small_vector, 8> vk_copies; for (size_t index = 0; index < buffers_span.size(); index++) { buffers_vector.emplace_back(buffers_span[index]); @@ -2359,17 +2250,12 @@ void Image::DownloadMemory(std::span buffers_span, std::span o } scheduler->RequestOutsideRenderPassOperationContext(); scheduler->Record([buffers = std::move(buffers_vector), image = *original_image, - aspect_mask_ = aspect_mask, vk_copies, - consumer_stages = runtime->device.GetBufferConsumerStages(), - consumer_access = runtime->device.GetBufferConsumerAccess()]( - vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier2 read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .srcAccessMask = vk::ACCESS_IMAGE_WRITES, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -2383,28 +2269,25 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(read_barrier); + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, read_barrier); for (size_t index = 0; index < buffers.size(); index++) { cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers[index], vk_copies[index]); } - const VkMemoryBarrier2 memory_write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + const VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = consumer_stages, - .dstAccessMask = consumer_access, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; - const VkImageMemoryBarrier2 image_write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .srcAccessMask = 0, - .dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS, - .dstAccessMask = vk::ACCESS_IMAGE_USERS, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -2418,7 +2301,8 @@ void Image::DownloadMemory(std::span buffers_span, std::span o .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(0, memory_write_barrier, {}, image_write_barrier); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + 0, memory_write_barrier, nullptr, image_write_barrier); }); } @@ -2442,19 +2326,11 @@ VkImageView Image::StorageImageView(s32 level) noexcept { if (!view) { auto format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); - VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; if (WillUseAcceleratedAstcDecode(runtime->device, info)) { format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; - } else if (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload)) { - format_info.format = UnswizzleStorageFormat(BytesPerBlock(info.format)); - if (info.type == ImageType::e3D) { - view_type = VK_IMAGE_VIEW_TYPE_3D; - } else if (info.type == ImageType::Linear) { - view_type = VK_IMAGE_VIEW_TYPE_2D; - } } - view = MakeStorageView(runtime->device.GetLogical(), level, *original_image, - format_info.format, view_type); + view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), + format_info.format); } return *view; } @@ -2646,9 +2522,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI if (device->ApiVersion() >= VK_API_VERSION_1_3) { const VkFormatProperties3 properties3 = device->GetPhysical().GetFormatProperties3(format_info.format); + supports_depth_comparison = + (properties3.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; supports_minmax_filter = (properties3.optimalTilingFeatures & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } else { + supports_depth_comparison = true; supports_minmax_filter = (device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; @@ -2917,6 +2797,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t has_added_anisotropy = max_anisotropy > default_anisotropy; has_linear_filtering = mag_filter == VK_FILTER_LINEAR || min_filter == VK_FILTER_LINEAR || mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR; + has_depth_comparison = tsc.depth_compare_enabled != 0; has_minmax_reduction = reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; has_srgb_border_color = tsc.srgb_conversion != 0 && srgb_border_color != border_color; if (has_minmax_reduction && !device.IsExtSamplerFilterMinmaxSupported()) { @@ -3075,6 +2956,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, ImageView* depth_buffer, bool is_rescaled_) { - std::array color_formats{}; - std::array color_views{}; - PixelFormat depth_format = PixelFormat::Invalid; - VkImageView depth_view = VK_NULL_HANDLE; + boost::container::small_vector attachments; + RenderPassKey renderpass_key{}; s32 num_layers = 1; is_rescaled = is_rescaled_; @@ -3103,15 +2985,15 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, for (size_t index = 0; index < NUM_RT; ++index) { const ImageView* const color_buffer = color_buffers[index]; if (!color_buffer) { - color_formats[index] = PixelFormat::Invalid; + renderpass_key.color_formats[index] = PixelFormat::Invalid; continue; } width = (std::min)(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) : color_buffer->size.width); height = (std::min)(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) : color_buffer->size.height); - color_views[index] = color_buffer->RenderTarget(); - color_formats[index] = color_buffer->format; + attachments.push_back(color_buffer->RenderTarget()); + renderpass_key.color_formats[index] = color_buffer->format; num_layers = (std::max)(num_layers, color_buffer->range.extent.layers); images[num_images] = color_buffer->ImageHandle(); image_ranges[num_images] = MakeSubresourceRange(color_buffer); @@ -3119,7 +3001,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, samples = color_buffer->Samples(); ++num_images; } - const size_t num_colors = num_images; + const size_t num_colors = attachments.size(); VkImage depth_image = VK_NULL_HANDLE; VkImageAspectFlags depth_aspect_mask = 0; if (depth_buffer) { @@ -3127,8 +3009,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, : depth_buffer->size.width); height = (std::min)(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) : depth_buffer->size.height); - depth_view = depth_buffer->RenderTarget(); - depth_format = depth_buffer->format; + attachments.push_back(depth_buffer->RenderTarget()); + renderpass_key.depth_format = depth_buffer->format; num_layers = (std::max)(num_layers, depth_buffer->range.extent.layers); images[num_images] = depth_buffer->ImageHandle(); const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer); @@ -3139,55 +3021,71 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; depth_image = depth_buffer->ImageHandle(); depth_aspect_mask = subresource_range.aspectMask; + } else { + renderpass_key.depth_format = PixelFormat::Invalid; } + renderpass_key.samples = samples; const bool do_resolve_color = ENABLE_MSAA_TILER_RESOLVE && samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); + renderpass_key.resolve_color = do_resolve_color; const bool do_resolve_depth_stencil = ENABLE_MSAA_TILER_RESOLVE && samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE && - runtime.device.IsTiler() && SupportsDepthStencilResolve(runtime.device, depth_format); + runtime.device.IsTiler() && + SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format); + renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil; + discard_msaa_color = + ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; + discard_msaa_depth_stencil = ENABLE_MSAA_RESOLVE_CONSUME && + ENABLE_MSAA_DEPTH_STENCIL_DISCARD && do_resolve_depth_stencil; + + renderpass = runtime.render_pass_cache.Get(renderpass_key); + render_pass_key = renderpass_key; + render_pass_cache = &runtime.render_pass_cache; runtime_ptr = &runtime; - id = ++runtime.framebuffer_serial; render_area.width = (std::min)(render_area.width, width); render_area.height = (std::min)(render_area.height, height); - const u32 layers = static_cast((std::max)(num_layers, 1)); - formats = MakeRenderingFormats(runtime.device, color_formats, depth_format); - attachments = MakeRenderingAttachments(formats, color_views, depth_view, - VkRect2D{.offset = {0, 0}, .extent = render_area}, - layers); - if (do_resolve_color) { + const u32 layers = static_cast((std::max)(num_layers, 1)); for (size_t index = 0; index < NUM_RT; ++index) { - if (color_formats[index] == PixelFormat::Invalid) { + const PixelFormat format = renderpass_key.color_formats[index]; + if (format == PixelFormat::Invalid) { continue; } + const VkFormat vk_format = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format; const VkImage msaa_image = images[rt_map[index]]; - VkRenderingAttachmentInfo& attachment = attachments.colors[index]; - attachment.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT; - if (VideoCore::Surface::IsPixelFormatInteger(color_formats[index])) { - attachment.resolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; - } - attachment.resolveImageView = runtime.GetOrCreateResolveShadow( - msaa_image, formats.colors[index], render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT); + attachments.push_back(runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); resolve_shadow_images[num_resolve_shadows++] = msaa_image; } } if (do_resolve_depth_stencil) { + const u32 layers = static_cast((std::max)(num_layers, 1)); const VkFormat vk_format = - MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, depth_format) + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + renderpass_key.depth_format) .format; - const VkImageView resolve_view = runtime.GetOrCreateResolveShadow( - depth_image, vk_format, render_area, layers, depth_aspect_mask); - const ResolveModes modes = PickResolveModes(runtime.device, depth_format); - attachments.depth.resolveMode = modes.depth; - attachments.depth.resolveImageView = resolve_view; - attachments.stencil.resolveMode = modes.stencil; - attachments.stencil.resolveImageView = resolve_view; + attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area, + layers, depth_aspect_mask)); resolve_shadow_images[num_resolve_shadows++] = depth_image; } + + num_color_buffers = static_cast(num_colors); + framebuffer = runtime.device.GetLogical().CreateFramebuffer({ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = renderpass, + .attachmentCount = static_cast(attachments.size()), + .pAttachments = attachments.data(), + .width = render_area.width, + .height = render_area.height, + .layers = static_cast((std::max)(num_layers, 1)), + }); } void Framebuffer::MarkResolveShadowsUpToDate() const { @@ -3199,69 +3097,86 @@ void Framebuffer::MarkResolveShadowsUpToDate() const { } } +VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, + u32 color_discard_mask, + bool depth_stencil_discard) const { + if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0 && + !depth_stencil_discard) { + return renderpass; + } + static_assert(NUM_RT <= 8); + const u32 variant_key = color_clear_mask | (color_discard_mask << 8) | + (static_cast(depth_stencil_clear) << 16) | + (static_cast(depth_stencil_discard) << 17); + for (u32 index = 0; index < num_memoized_variants; ++index) { + if (variant_keys[index] == variant_key) { + return variant_render_passes[index]; + } + } + RenderPassKey key = render_pass_key; + key.color_clear_mask = color_clear_mask; + key.depth_stencil_clear = depth_stencil_clear; + key.color_discard_mask = color_discard_mask; + key.depth_stencil_discard = depth_stencil_discard; + const VkRenderPass variant = render_pass_cache->Get(key); + if (num_memoized_variants < variant_keys.size()) { + variant_keys[num_memoized_variants] = variant_key; + variant_render_passes[num_memoized_variants] = variant; + ++num_memoized_variants; + } + return variant; +} + void TextureCacheRuntime::AccelerateImageUpload( Image& image, const StagingBufferRef& map, - std::span swizzles) { - const bool is_rescaled = True(image.flags & ImageFlagBits::Rescaled); - if (is_rescaled) { - image.ScaleDown(true); - } - const bool reorder = !is_rescaled && True(image.flags & ImageFlagBits::ReorderableUpload); + std::span swizzles, + u32 z_start, u32 z_count) { + if (IsPixelFormatASTC(image.info.format)) { - astc_decoder_pass->Assemble(image, map, swizzles, reorder); - } else if (image.info.type == ImageType::e3D) { - bl_unswizzle_3d_pass->Unswizzle(image, map, swizzles, reorder); - } else if (image.info.type == ImageType::Linear) { - pitch_unswizzle_pass->Unswizzle(image, map, swizzles, reorder); - } else { - bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles, reorder); + return astc_decoder_pass->Assemble(image, map, swizzles); } - if (is_rescaled) { - image.ScaleUp(); - } -} -void TextureCacheRuntime::ImportGuestMemory(const Common::HostMemory& host_memory) { - guest_memory.emplace(device, memory_allocator, scheduler, host_memory); - if (guest_memory->Empty()) { - guest_memory.reset(); + if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { + if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { + ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); + } + ASSERT(false); + return; } -} -std::optional TextureCacheRuntime::GuestMemorySource(const u8* pointer, - size_t size) const { - if (pointer == nullptr) { - return std::nullopt; + if (bl3d_unswizzle_pass && IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D && image.info.resources.levels == 1 && image.info.resources.layers == 1) { + return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles, z_start, z_count); } - const std::optional range = guest_memory->Find(pointer, size); - if (!range || range->offset % device.GetStorageBufferAlignment() != 0) { - return std::nullopt; - } - return StagingBufferRef{.buffer = range->buffer, .offset = range->offset}; + + ASSERT(false); } void TextureCacheRuntime::TransitionImageLayout(Image& image) { - if (image.ExchangeInitialization()) { - return; + if (!image.ExchangeInitialization()) { + VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_NONE, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image.Handle(), + .subresourceRange{ + .aspectMask = image.AspectMask(), + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier); + }); } - if (!image.CanUploadHostMemory()) { - RecordInitialLayout(scheduler, image.Handle(), image.AspectMask()); - return; - } - device.GetLogical().TransitionImageLayoutEXT(VkHostImageLayoutTransitionInfoEXT{ - .sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT, - .pNext = nullptr, - .image = image.Handle(), - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .subresourceRange{ - .aspectMask = image.AspectMask(), - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 37b476a489..2e9736f707 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -6,14 +6,12 @@ #pragma once -#include #include #include "video_core/texture_cache/texture_cache_base.h" #include "shader_recompiler/shader_info.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" -#include "video_core/renderer_vulkan/vk_guest_memory.h" #include "video_core/renderer_vulkan/vk_render_pass_cache.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/texture_cache/image_view_base.h" @@ -38,6 +36,7 @@ class Device; class Image; class ImageView; class Framebuffer; +class RenderPassCache; class StagingBufferPool; class Scheduler; @@ -47,12 +46,13 @@ public: MemoryAllocator& memory_allocator_, StagingBufferPool& staging_buffer_pool_, BlitImageHelper& blit_image_helper_, + RenderPassCache& render_pass_cache_, DescriptorPool& descriptor_pool, ComputePassDescriptorQueue& compute_pass_descriptor_queue); void Finish(); - StagingBufferRef UploadStagingBuffer(size_t size); + StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); @@ -60,6 +60,8 @@ public: void TickFrame(); + void FlushDeferredClear(); + u64 GetDeviceLocalMemory() const; u64 GetDeviceMemoryUsage() const; @@ -87,21 +89,17 @@ public: void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view); + bool CanAccelerateImageUpload(Image&) const noexcept { + return false; + } + bool CanUploadMSAA() const noexcept { return true; } void AccelerateImageUpload(Image&, const StagingBufferRef&, - std::span); - - void ImportGuestMemory(const Common::HostMemory& host_memory); - - [[nodiscard]] bool HasGuestMemory() const noexcept { - return guest_memory.has_value(); - } - - [[nodiscard]] std::optional GuestMemorySource(const u8* pointer, - size_t size) const; + std::span, + u32 z_start, u32 z_count); void InsertUploadMemoryBarrier() {} @@ -141,8 +139,6 @@ public: void EraseResolveShadow(VkImage msaa_image); - bool CopyResolveShadow(VkImage msaa_image, VkImage dst_image, const VkImageCopy2& region); - std::span ViewFormats(PixelFormat format) { return view_formats[static_cast(format)]; } @@ -159,14 +155,12 @@ public: MemoryAllocator& memory_allocator; StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; + RenderPassCache& render_pass_cache; std::optional astc_decoder_pass; - std::optional bl_unswizzle_2d_pass; - std::optional bl_unswizzle_3d_pass; - std::optional pitch_unswizzle_pass; - std::optional guest_memory; + + std::optional bl3d_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; - std::bitset host_copy_formats; static constexpr size_t indexing_slots = 8 * sizeof(size_t); std::array buffers{}; @@ -194,7 +188,6 @@ public: std::vector msaa_scratch_images; ::Common::unordered_map resolve_shadows; std::vector> pending_resolve_shadows; - u64 framebuffer_serial{}; }; class Framebuffer { @@ -217,18 +210,22 @@ public: std::span color_buffers, ImageView* depth_buffer, bool is_rescaled = false); - [[nodiscard]] u64 Id() const noexcept { - return id; + [[nodiscard]] VkFramebuffer Handle() const noexcept { + return *framebuffer; } - [[nodiscard]] const RenderingFormats& Formats() const noexcept { - return formats; + [[nodiscard]] VkRenderPass RenderPass() const noexcept { + return renderpass; } - [[nodiscard]] const RenderingAttachments& Attachments() const noexcept { - return attachments; + [[nodiscard]] const RenderPassKey& RenderPassKeyBase() const noexcept { + return render_pass_key; } + [[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear, + u32 color_discard_mask, + bool depth_stencil_discard) const; + [[nodiscard]] VkExtent2D RenderArea() const noexcept { return render_area; } @@ -237,6 +234,10 @@ public: return samples; } + [[nodiscard]] u32 NumColorBuffers() const noexcept { + return num_color_buffers; + } + [[nodiscard]] u32 NumImages() const noexcept { return num_images; } @@ -265,16 +266,26 @@ public: return is_rescaled; } + [[nodiscard]] bool DiscardsMsaaColor() const noexcept { + return discard_msaa_color; + } + + [[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept { + return discard_msaa_depth_stencil; + } + /// Records that a render pass has begun, so its resolve attachments will hold valid contents /// once it ends. void MarkResolveShadowsUpToDate() const; private: - RenderingFormats formats{}; - RenderingAttachments attachments{}; - u64 id{}; + static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8; + + vk::Framebuffer framebuffer; + VkRenderPass renderpass{}; VkExtent2D render_area{}; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + u32 num_color_buffers = 0; u32 num_images = 0; std::array images{}; std::array image_ranges{}; @@ -285,6 +296,13 @@ private: std::array resolve_shadow_images{}; u32 num_resolve_shadows = 0; TextureCacheRuntime* runtime_ptr{nullptr}; + RenderPassKey render_pass_key{}; + RenderPassCache* render_pass_cache{nullptr}; + bool discard_msaa_color{}; + bool discard_msaa_depth_stencil{}; + mutable std::array variant_keys{}; + mutable std::array variant_render_passes{}; + mutable u32 num_memoized_variants{}; }; class Image : public VideoCommon::ImageBase { @@ -307,13 +325,6 @@ public: void UploadMemory(const StagingBufferRef& map, std::span copies); - void UploadHostMemory(std::span memory, - std::span copies); - - [[nodiscard]] bool CanUploadHostMemory() const noexcept { - return (original_image.UsageFlags() & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) != 0; - } - void DownloadMemory(VkBuffer buffer, size_t offset, std::span copies); @@ -323,6 +334,8 @@ public: void DownloadMemory(const StagingBufferRef& map, std::span copies); + void AllocateComputeUnswizzleImage(); + [[nodiscard]] VkImage Handle() const noexcept { return *(this->*current_image); } @@ -348,6 +361,10 @@ public: bool ScaleDown(bool ignore = false); + u64 allocation_tick; + + friend class BlockLinearUnswizzle3DPass; + private: bool BlitScaleHelper(bool scale_up); @@ -359,6 +376,12 @@ private: vk::Image original_image; vk::Image scaled_image; + vk::Buffer compute_unswizzle_buffer; + VkDeviceSize compute_unswizzle_buffer_size = 0; + bool has_compute_unswizzle_buffer = false; + + void AllocateComputeUnswizzleBuffer(u32 max_slices); + // Use a pointer to field because it is relative, so that the object can be // moved without breaking the reference. vk::Image Image::*current_image{}; @@ -417,6 +440,10 @@ public: return samples; } + [[nodiscard]] bool SupportsDepthComparison() const noexcept { + return supports_depth_comparison; + } + [[nodiscard]] bool RequiresBorderColorFormat() const noexcept { return requires_border_color_format; } @@ -467,6 +494,7 @@ private: VkComponentMapping swizzle_mapping{}; + bool supports_depth_comparison = false; bool requires_border_color_format = false; bool supports_minmax_filter = false; bool has_identity_swizzle = true; @@ -543,6 +571,7 @@ private: bool has_added_anisotropy{}; bool has_linear_filtering{}; + bool has_depth_comparison{}; bool has_minmax_reduction{}; bool has_custom_border_colors{}; bool has_srgb_border_color{}; @@ -550,6 +579,7 @@ private: }; struct TextureCacheParams { + static constexpr bool ENABLE_VALIDATION = true; static constexpr bool FRAMEBUFFER_BLITS = false; static constexpr bool HAS_EMULATED_COPIES = false; static constexpr bool HAS_DEVICE_MEMORY_INFO = true; diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp index 8b18ae7d51..095908b6cf 100644 --- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp @@ -208,22 +208,16 @@ void TurboMode::Run(std::stop_token stop_token) { // Finish. cmdbuf.End(); - const VkCommandBufferSubmitInfo cmdbuf_info{ - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + const VkSubmitInfo submit_info{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, - .commandBuffer = *cmdbuf, - .deviceMask = 0, - }; - const VkSubmitInfo2 submit_info{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, - .pNext = nullptr, - .flags = 0, - .waitSemaphoreInfoCount = 0, - .pWaitSemaphoreInfos = nullptr, - .commandBufferInfoCount = 1, - .pCommandBufferInfos = &cmdbuf_info, - .signalSemaphoreInfoCount = 0, - .pSignalSemaphoreInfos = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = cmdbuf.address(), + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, }; m_device.GetGraphicsQueue().Submit(std::array{submit_info}, *fence); diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index 1efe39180f..9f30e65dbd 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -40,13 +40,17 @@ class UpdateDescriptorQueue final { static constexpr size_t FRAMES_IN_FLIGHT = 8; public: - static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x20000; - static constexpr size_t COMPUTE_FRAME_PAYLOAD_SIZE = 0x8000; + static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x80000; + static constexpr size_t COMPUTE_FRAME_PAYLOAD_SIZE = 0x20000; explicit UpdateDescriptorQueue(const Device& device_, size_t frame_payload_size_, bool supports_descriptor_buffer_ = false); ~UpdateDescriptorQueue(); + [[nodiscard]] bool UsesDescriptorBuffer() const noexcept { + return use_descriptor_buffer; + } + void TickFrame(); void Acquire(Scheduler& scheduler, size_t required_entries = 0, bool use_descriptor_buffer_ = false); diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index d2d6ffeca1..f1aff2810f 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -49,7 +49,6 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { return last_shaders_valid; } dirty[VideoCommon::Dirty::Shaders] = false; - fragment_color_outputs = 0; const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()}; for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) { @@ -79,9 +78,6 @@ bool ShaderCache::RefreshStages(std::array& unique_hashes) { } shader_infos[index] = shader_info; unique_hashes[index] = shader_info->unique_hash; - if (program == Tegra::Engines::Maxwell3D::Regs::ShaderType::Pixel) { - fragment_color_outputs = shader_info->color_outputs; - } } last_shaders_valid = true; return true; @@ -245,13 +241,6 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu info->unique_hash = env.CalculateHash(); info->size_bytes = env.ReadSizeBytes(); } - if (env.ShaderStage() == Shader::Stage::Fragment) { - for (u32 target = 0; target < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++target) { - if (env.SPH().ps.HasOutputComponents(target)) { - info->color_outputs |= 1U << target; - } - } - } const size_t size_bytes{info->size_bytes}; const ShaderInfo* const result{info.get()}; Register(std::move(info), cpu_addr, size_bytes); diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index aec003d166..997f2b0371 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -36,7 +36,6 @@ class GenericEnvironment; struct ShaderInfo { u64 unique_hash{}; size_t size_bytes{}; - u32 color_outputs{}; }; class ShaderCache : public VideoCommon::ChannelSetupCaches { @@ -99,7 +98,6 @@ protected: std::array shader_infos{}; bool last_shaders_valid = false; - u32 fragment_color_outputs{}; private: /// @brief Tries to obtain a cached shader starting in a given address diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index d1b9bf4151..4603870bfa 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/math_util.h" +#include "common/settings.h" #include "video_core/surface.h" namespace VideoCore::Surface { @@ -343,6 +344,10 @@ bool IsPixelFormatETC2(PixelFormat format) { case PixelFormat::ETC2_RGB_SRGB: case PixelFormat::ETC2_RGBA_SRGB: case PixelFormat::ETC2_RGB_PTA_SRGB: + case PixelFormat::EAC_R11_UNORM: + case PixelFormat::EAC_R11_SNORM: + case PixelFormat::EAC_R11G11_UNORM: + case PixelFormat::EAC_R11G11_SNORM: return true; default: return false; @@ -464,7 +469,14 @@ u64 TranscodedAstcSize(u64 base_size, PixelFormat format) { static_cast(DefaultBlockHeight(format)) * RGBA8_PIXEL_SIZE; const u64 uncompressed_size = (base_size * base_block_size) / BytesPerBlock(format); - return uncompressed_size; + switch (Settings::values.astc_recompression.GetValue()) { + case Settings::AstcRecompression::Bc1: + return uncompressed_size / 8; + case Settings::AstcRecompression::Bc3: + return uncompressed_size / 4; + default: + return uncompressed_size; + } } } // namespace VideoCore::Surface diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 5611dbdffa..8e1973d3b9 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -119,6 +119,10 @@ namespace VideoCore::Surface { PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \ PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \ PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(EAC_R11_UNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(EAC_R11_SNORM, 4, 4, 64) \ + PIXEL_FORMAT_ELEM(EAC_R11G11_UNORM, 4, 4, 128) \ + PIXEL_FORMAT_ELEM(EAC_R11G11_SNORM, 4, 4, 128) \ /* Depth formats */ \ PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \ PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \ diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h index 37022842fe..e40c128ab5 100644 --- a/src/video_core/texture_cache/descriptor_table.h +++ b/src/video_core/texture_cache/descriptor_table.h @@ -7,16 +7,12 @@ #pragma once #include -#include -#include -#include #include #include "common/alignment.h" #include "common/common_types.h" #include "common/div_ceil.h" #include "common/assert.h" -#include "common/slot_vector.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -25,55 +21,53 @@ namespace VideoCommon { template class DescriptorTable { public: - struct Entry { - T descriptor; - Common::SlotId id; - u32 generation; - }; - - void Synchronize(GPUVAddr gpu_addr, u32 limit) noexcept { - if (current_gpu_addr != gpu_addr || current_limit != limit) { + [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) noexcept { + bool ret = !(current_gpu_addr == gpu_addr && current_limit == limit); + if (ret) { Refresh(gpu_addr, limit); } + return ret; } void Invalidate() noexcept { - ++generation; + std::ranges::fill(read_descriptors, 0); } - [[nodiscard]] std::pair Read(Tegra::MemoryManager const& gpu_memory, u32 index) noexcept { + [[nodiscard]] std::pair Read(Tegra::MemoryManager const& gpu_memory, u32 index) noexcept { DEBUG_ASSERT(index <= current_limit); const GPUVAddr gpu_addr = current_gpu_addr + index * sizeof(T); - T value{}; - if (!aligned) { - gpu_memory.ReadBlockUnsafe(gpu_addr, std::addressof(value), sizeof(T)); - } else if (const u8* const ptr = gpu_memory.GetPointer(gpu_addr)) { - std::memcpy(std::addressof(value), ptr, sizeof(T)); + std::pair result; + gpu_memory.ReadBlockUnsafe(gpu_addr, std::addressof(result.first), sizeof(T)); + if ((read_descriptors[index / 64] & (1ULL << (index % 64))) != 0) { + result.second = result.first != descriptors[index]; + } else { + read_descriptors[index / 64] |= 1ULL << (index % 64); + result.second = true; } - Entry& entry = entries[index]; - const bool is_new = entry.generation != generation || entry.descriptor != value; - if (is_new) { - entry.descriptor = value; - entry.generation = generation; + if (result.second) { + descriptors[index] = result.first; } - return {entry, is_new}; + return result; } void Refresh(GPUVAddr gpu_addr, u32 limit) noexcept { current_gpu_addr = gpu_addr; current_limit = limit; - aligned = gpu_addr % sizeof(T) == 0; - ++generation; - if (entries.size() <= limit) { - entries.resize(std::bit_ceil(size_t{limit} + 1)); - } + // Mario Brothership reallocates a lot of times, so use aggressive pre-alloc sizes + // std::vector by default uses quadratic growth, but that isn't even enough to satisfy brothership + const size_t num_descriptors = ((limit + 0x80000) & (~0x7ffff)) + 1; + size_t old_size = read_descriptors.size(); + read_descriptors.resize(Common::DivCeil(num_descriptors, 64U)); + old_size = (std::min)(old_size, read_descriptors.size()); + std::fill(read_descriptors.begin(), read_descriptors.begin() + old_size, 0); + // + descriptors.resize(num_descriptors); } - std::vector entries; + std::vector read_descriptors; + std::vector descriptors; GPUVAddr current_gpu_addr{}; u32 current_limit{}; - u32 generation{1}; - bool aligned{}; }; } // namespace VideoCommon diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index ea68a1c063..d6e019cddf 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp @@ -204,6 +204,15 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, return PixelFormat::ETC2_RGB_PTA_SRGB; case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB): return PixelFormat::ETC2_RGBA_SRGB; + /* EAC */ + case Hash(TextureFormat::EAC, UNORM): + return PixelFormat::EAC_R11_UNORM; + case Hash(TextureFormat::EAC, SNORM): + return PixelFormat::EAC_R11_SNORM; + case Hash(TextureFormat::EACX2, UNORM): + return PixelFormat::EAC_R11G11_UNORM; + case Hash(TextureFormat::EACX2, SNORM): + return PixelFormat::EAC_R11G11_SNORM; /* ASTC */ case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR): return PixelFormat::ASTC_2D_4X4_UNORM; diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 3c597a7f1d..79fc8505d6 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -45,7 +45,6 @@ enum class ImageFlagBits : u32 { AsynchronousDecode = 1 << 16, IsDecoding = 1 << 17, ///< Is currently being decoded asynchronously. - ReorderableUpload = 1 << 18, }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -96,7 +95,6 @@ struct ImageBase { u32 scale_rating = 0; u64 scale_tick = 0; bool has_scaled = false; - u32 guest_data_checks = 0; size_t channel = 0; @@ -107,7 +105,6 @@ struct ImageBase { VAddr cpu_addr_end = 0; u64 modification_tick = 0; - u64 guest_data_hash = 0; size_t lru_index = SIZE_MAX; std::array mip_level_offsets{}; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 2e26880868..e92e54f8ce 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -8,11 +8,11 @@ #include #include +#include #include "common/container/unordered_map.h" #include #include "common/alignment.h" -#include "common/cityhash.h" #include "common/settings.h" #include "common/slot_vector.h" #include "video_core/control/channel_state.h" @@ -58,16 +58,59 @@ TextureCache

::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag void(slot_samplers.insert(runtime, sampler_descriptor)); if constexpr (HAS_DEVICE_MEMORY_INFO) { - device_local_memory = runtime.GetDeviceLocalMemory(); + const s64 device_local_memory = static_cast(runtime.GetDeviceLocalMemory()); + const s64 min_spacing_expected = device_local_memory - 1_GiB; + const s64 min_spacing_critical = device_local_memory - 512_MiB; + const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD); + const s64 min_vacancy_expected = (6 * mem_threshold) / 10; + const s64 min_vacancy_critical = (2 * mem_threshold) / 10; + expected_memory = static_cast( + (std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected), + DEFAULT_EXPECTED_MEMORY)); + critical_memory = static_cast( + (std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical), + DEFAULT_CRITICAL_MEMORY)); + minimum_memory = static_cast((device_local_memory - mem_threshold) / 2); + } else { + expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; + critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; + minimum_memory = 0; + } + + const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue(); + + if (gpu_unswizzle_enabled) { + switch (Settings::values.gpu_unswizzle_texture_size.GetValue()) { + case Settings::GpuUnswizzleSize::VerySmall: gpu_unswizzle_maxsize = 16_MiB; break; + case Settings::GpuUnswizzleSize::Small: gpu_unswizzle_maxsize = 32_MiB; break; + case Settings::GpuUnswizzleSize::Normal: gpu_unswizzle_maxsize = 128_MiB; break; + case Settings::GpuUnswizzleSize::Large: gpu_unswizzle_maxsize = 256_MiB; break; + case Settings::GpuUnswizzleSize::VeryLarge: gpu_unswizzle_maxsize = 512_MiB; break; + default: gpu_unswizzle_maxsize = 128_MiB; break; + } + + switch (Settings::values.gpu_unswizzle_stream_size.GetValue()) { + case Settings::GpuUnswizzle::VeryLow: swizzle_chunk_size = 4_MiB; break; + case Settings::GpuUnswizzle::Low: swizzle_chunk_size = 8_MiB; break; + case Settings::GpuUnswizzle::Normal: swizzle_chunk_size = 16_MiB; break; + case Settings::GpuUnswizzle::Medium: swizzle_chunk_size = 32_MiB; break; + case Settings::GpuUnswizzle::High: swizzle_chunk_size = 64_MiB; break; + default: swizzle_chunk_size = 16_MiB; + } + + switch (Settings::values.gpu_unswizzle_chunk_size.GetValue()) { + case Settings::GpuUnswizzleChunk::VeryLow: swizzle_slices_per_batch = 32; break; + case Settings::GpuUnswizzleChunk::Low: swizzle_slices_per_batch = 64; break; + case Settings::GpuUnswizzleChunk::Normal: swizzle_slices_per_batch = 128; break; + case Settings::GpuUnswizzleChunk::Medium: swizzle_slices_per_batch = 256; break; + case Settings::GpuUnswizzleChunk::High: swizzle_slices_per_batch = 512; break; + default: swizzle_slices_per_batch = 128; + } + } else { + gpu_unswizzle_maxsize = 0; + swizzle_chunk_size = 0; + swizzle_slices_per_batch = 0; } - const auto thresholds = VideoCommon::MakeReclaimThresholds( - device_local_memory, static_cast(TARGET_THRESHOLD), - static_cast(DEFAULT_EXPECTED_MEMORY), static_cast(DEFAULT_CRITICAL_MEMORY), - HEAP_PRESSURE_HEADROOM); - minimum_memory = thresholds.minimum; - expected_memory = thresholds.expected; - critical_memory = thresholds.critical; - heap_headroom = thresholds.headroom; } template @@ -77,9 +120,8 @@ void TextureCache

::RunGarbageCollector() { u64 ticks_to_destroy = 0; size_t num_iterations = 0; const auto Configure = [&](bool allow_aggressive) { - high_priority_mode = heap_pressure || total_used_memory >= expected_memory; - aggressive_mode = - allow_aggressive && (heap_pressure || total_used_memory >= critical_memory); + high_priority_mode = total_used_memory >= expected_memory; + aggressive_mode = allow_aggressive && total_used_memory >= critical_memory; ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL; num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10); }; @@ -93,12 +135,12 @@ void TextureCache

::RunGarbageCollector() { return false; } const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap); - if (!aggressive_mode && (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) { + if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) { return false; } if (must_download) { auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, swizzle_data_buffer); @@ -119,7 +161,7 @@ void TextureCache

::RunGarbageCollector() { }; Configure(false); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); - if (heap_pressure || total_used_memory >= critical_memory) { + if (total_used_memory >= critical_memory) { Configure(true); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); } @@ -127,17 +169,18 @@ void TextureCache

::RunGarbageCollector() { template void TextureCache

::TickFrame() { - heap_pressure = false; - if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) { - heap_pressure = runtime.GetDeviceMemoryUsage() + heap_headroom >= device_local_memory; + // If we can obtain the memory info, use it instead of the estimate. + if (runtime.CanReportMemoryUsage()) { + total_used_memory = runtime.GetDeviceMemoryUsage(); } - if (total_used_memory > minimum_memory || heap_pressure) { + if (total_used_memory > minimum_memory) { RunGarbageCollector(); } sentenced_images.Tick(); sentenced_framebuffers.Tick(); sentenced_image_view.Tick(); TickAsyncDecode(); + TickAsyncUnswizzle(); runtime.TickFrame(); ++frame_tick; @@ -196,16 +239,65 @@ void TextureCache

::FillImageViews(std::span views, bool compu template void TextureCache

::CheckFeedbackLoop(std::span views) { - if (!rt_depth_image_id || !Settings::values.barrier_feedback_loops.GetValue()) { + if (!Settings::values.barrier_feedback_loops.GetValue()) { return; } - const ImageViewId depth_view_id = render_targets.depth_buffer_id; - for (const auto& view : views) { - if (view.id && view.id != depth_view_id && - slot_image_views[view.id].image_id == rt_depth_image_id) { + + if (render_targets_serial == last_feedback_loop_serial && + texture_bindings_serial == last_feedback_texture_serial) { + if (last_feedback_loop_result) { runtime.BarrierFeedbackLoop(); - return; } + return; + } + + if (rt_active_mask == 0) { + last_feedback_loop_serial = render_targets_serial; + last_feedback_texture_serial = texture_bindings_serial; + last_feedback_loop_result = false; + return; + } + const u32 depth_bit = 1u << NUM_RT; + const bool depth_active = (rt_active_mask & depth_bit) != 0; + + const bool requires_barrier = [&] { + for (const auto& view : views) { + if (!view.id) { + continue; + } + + { + bool is_continue = false; + for (size_t i = 0; i < 8; ++i) + is_continue |= (rt_active_mask & (1u << i)) && view.id == render_targets.color_buffer_ids[i]; + if (is_continue) + continue; + } + + if (depth_active && view.id == render_targets.depth_buffer_id) + continue; + + const ImageId view_image_id = slot_image_views[view.id].image_id; + { + bool is_continue = false; + for (size_t i = 0; i < 8; ++i) + is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i]; + if (is_continue) + continue; + } + if (depth_active && view_image_id == rt_depth_image_id) { + return true; + } + } + + return false; + }(); + + last_feedback_loop_serial = render_targets_serial; + last_feedback_texture_serial = texture_bindings_serial; + last_feedback_loop_result = requires_barrier; + if (requires_barrier) { + runtime.BarrierFeedbackLoop(); } } @@ -221,11 +313,14 @@ SamplerId TextureCache

::GetSamplerId(u32 index, bool compute) { LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); return NULL_SAMPLER_ID; } - auto const [entry, is_new] = table.Read(*gpu_memory, index); + auto const map_index = index | (compute ? Common::SlotId::TAGGED_VALUE : 0); + auto const [descriptor, is_new] = table.Read(*gpu_memory, index); if (is_new) { - entry.id = FindSampler(entry.descriptor, compute); + auto const id = FindSampler(descriptor, compute); + channel_state->sampler_ids.insert_or_assign(map_index, id); + return id; } - return entry.id; + return channel_state->sampler_ids.find(map_index)->second; } template @@ -244,14 +339,26 @@ void TextureCache

::SynchronizeDescriptors(bool compute) { const bool linked_tsc = kepler_compute->launch_description.linked_tsc; const u32 tic_limit = kepler_compute->regs.tic.limit; const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit; - channel_state->compute_sampler_table.Synchronize(kepler_compute->regs.tsc.Address(), tsc_limit); - channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), tic_limit); + bool bindings_changed = false; + if (channel_state->compute_sampler_table.Synchronize(kepler_compute->regs.tsc.Address(), tsc_limit)) + bindings_changed = true; + if (channel_state->compute_image_table.Synchronize(kepler_compute->regs.tic.Address(), tic_limit)) + bindings_changed = true; + if (bindings_changed) { + ++texture_bindings_serial; + } } else { const bool linked_tsc = maxwell3d->regs.sampler_binding == Tegra::Engines::Maxwell3D::Regs::SamplerBinding::ViaHeaderBinding; const u32 tic_limit = maxwell3d->regs.tex_header.limit; const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tex_sampler.limit; - channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), tsc_limit); - channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), tic_limit); + bool bindings_changed = false; + if (channel_state->graphics_sampler_table.Synchronize(maxwell3d->regs.tex_sampler.Address(), tsc_limit)) + bindings_changed = true; + if (channel_state->graphics_image_table.Synchronize(maxwell3d->regs.tex_header.Address(), tic_limit)) + bindings_changed = true; + if (bindings_changed) { + ++texture_bindings_serial; + } } } @@ -381,9 +488,19 @@ void TextureCache

::UpdateRenderTargets(bool is_clear) { PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); - rt_depth_image_id = {}; + rt_active_mask = 0; + rt_image_id = {}; + for (size_t i = 0; i < rt_image_id.size(); ++i) { + if (ImageViewId const view = render_targets.color_buffer_ids[i]; view) { + rt_active_mask |= 1u << i; + rt_image_id[i] = slot_image_views[view].image_id; + } + } if (depth_buffer_id) { + rt_active_mask |= (1u << NUM_RT); rt_depth_image_id = slot_image_views[depth_buffer_id].image_id; + } else { + rt_depth_image_id = ImageId{}; } for (size_t index = 0; index < NUM_RT; ++index) { @@ -426,19 +543,26 @@ ImageViewId TextureCache

::VisitImageView(u32 index, bool compute) { LOG_DEBUG(HW_GPU, "Invalid image view index={}", index); return NULL_IMAGE_VIEW_ID; } - auto const [entry, is_new] = table.Read(*gpu_memory, index); + auto const map_index = index | (compute ? Common::SlotId::TAGGED_VALUE : 0); + // Is new (on the tegra engine side)? + auto const [descriptor, is_new] = table.Read(*gpu_memory, index); if (is_new) { - entry.id = NULL_IMAGE_VIEW_ID; - if (IsValidEntry(*gpu_memory, entry.descriptor)) { - const auto [pair, is_new_tc] = channel_state->image_views.try_emplace(entry.descriptor); + if (IsValidEntry(*gpu_memory, descriptor)) { + // Is new (registered view) on the texture cache side? + const auto [pair, is_new_tc] = channel_state->image_views.try_emplace(descriptor); if (is_new_tc) - pair->second = CreateImageView(entry.descriptor); - entry.id = pair->second; + pair->second = CreateImageView(descriptor); + PrepareImageView(pair->second, false, false); + channel_state->image_view_ids.insert_or_assign(map_index, pair->second); + return pair->second; } + channel_state->image_view_ids.insert_or_assign(map_index, NULL_IMAGE_VIEW_ID); + return NULL_IMAGE_VIEW_ID; } - if (entry.id != NULL_IMAGE_VIEW_ID) - PrepareImageView(entry.id, false, false); - return entry.id; + auto const it = channel_state->image_view_ids.find(map_index); + if (it->second != NULL_IMAGE_VIEW_ID) + PrepareImageView(it->second, false, false); + return it->second; } template @@ -453,6 +577,7 @@ FramebufferId TextureCache

::GetFramebufferId(const RenderTargets& key) { return id ? &slot_image_views[id] : nullptr; }); ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr; + runtime.FlushDeferredClear(); framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key); return framebuffer_id; } @@ -504,7 +629,7 @@ void TextureCache

::DownloadMemory(DAddr cpu_addr, size_t size) { for (const ImageId image_id : images) { Image& image = slot_images[image_id]; auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(map, copies); runtime.Finish(); SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, @@ -768,7 +893,7 @@ void TextureCache

::CommitAsyncFlushes() { for (const PendingDownload& download_info : download_ids) { if (download_info.is_swizzle) { Image& image = slot_images[download_info.object_id]; - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(download_map, copies); download_map.offset += Common::AlignUp(image.unswizzled_size_bytes, 64); } @@ -801,7 +926,7 @@ void TextureCache

::PopAsyncFlushes() { auto& download_buffer = download_map[download_info.async_buffer_id]; if (download_info.is_swizzle) { const ImageBase& image = slot_images[download_info.object_id]; - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); download_buffer.offset -= Common::AlignUp(image.unswizzled_size_bytes, 64); std::span download_span = download_buffer.mapped_span.subspan(download_buffer.offset); @@ -839,7 +964,7 @@ void TextureCache

::PopAsyncFlushes() { continue; } Image& image = slot_images[download_info.object_id]; - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); image.DownloadMemory(download_map, copies); download_map.offset += image.unswizzled_size_bytes; } @@ -852,7 +977,7 @@ void TextureCache

::PopAsyncFlushes() { continue; } const ImageBase& image = slot_images[download_info.object_id]; - const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); + const auto copies = FullDownloadCopies(image.info); SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span, swizzle_data_buffer); download_map.offset += image.unswizzled_size_bytes; download_span = download_span.subspan(image.unswizzled_size_bytes); @@ -986,29 +1111,6 @@ void TextureCache

::DownloadImageIntoBuffer(typename TextureCache

::Image* i } } -template -bool TextureCache

::IsAstcDataUnchanged(Image& image) { - static constexpr u32 CHECK_THRESHOLD = 4; - if (False(image.flags & ImageFlagBits::Converted) || - True(image.flags & ImageFlagBits::GpuModified) || - !IsPixelFormatASTC(image.info.format)) { - return false; - } - if (image.guest_data_checks < CHECK_THRESHOLD) { - ++image.guest_data_checks; - return false; - } - Tegra::Memory::GpuGuestMemory guest_data( - *gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer); - const u64 hash = Common::CityHash64(reinterpret_cast(guest_data.data()), - image.guest_size_bytes); - if (image.guest_data_hash == hash) { - return true; - } - image.guest_data_hash = hash; - return false; -} - template void TextureCache

::RefreshContents(Image& image, ImageId image_id) { if (False(image.flags & ImageFlagBits::CpuModified)) { @@ -1020,50 +1122,29 @@ void TextureCache

::RefreshContents(Image& image, ImageId image_id) { TrackImage(image, image_id); - if (IsAstcDataUnchanged(image)) { - return; - } - if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) { LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); runtime.TransitionImageLayout(image); return; } - gpu_memory->FlushRegion(image.gpu_addr, image.guest_size_bytes, - VideoCommon::CacheType::NoTextureCache); if (True(image.flags & ImageFlagBits::AsynchronousDecode)) { QueueAsyncDecode(image, image_id); return; } - if constexpr (requires { runtime.GuestMemorySource(nullptr, size_t{}); }) { - if (True(image.flags & ImageFlagBits::AcceleratedUpload) && runtime.HasGuestMemory() && - gpu_memory->IsContinuousRange(image.gpu_addr, image.guest_size_bytes)) { - const std::optional device_addr = gpu_memory->GpuToCpuAddress(image.gpu_addr); - if (device_addr) { - const auto source = runtime.GuestMemorySource( - device_memory.GetSpan(*device_addr, image.guest_size_bytes), - image.guest_size_bytes); - if (source) { - runtime.AccelerateImageUpload( - image, *source, FixSmallVectorADL(FullUploadSwizzles(image.info))); - return; - } - } - } - } - if constexpr (requires { image.UploadHostMemory(unswizzle_data_buffer, {}); }) { - if (True(image.flags & ImageFlagBits::ReorderableUpload) && image.CanUploadHostMemory()) { - Tegra::Memory::GpuGuestMemory - swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, - &swizzle_data_buffer); - unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); - const auto copies = FixSmallVectorADL(UnswizzleImage( - *gpu_memory, image.gpu_addr, image.info, swizzle_data, unswizzle_data_buffer)); - image.UploadHostMemory(unswizzle_data_buffer, copies); - return; - } - } + const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue(); + + if (gpu_unswizzle_enabled && + IsPixelFormatBCn(image.info.format) && + image.info.type == ImageType::e3D && + image.info.resources.levels == 1 && + image.info.resources.layers == 1 && + MapSizeBytes(image) >= gpu_unswizzle_maxsize && + False(image.flags & ImageFlagBits::GpuModified)) { + + QueueAsyncUnswizzle(image, image_id); + return; + } auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); UploadImageContents(image, staging); runtime.InsertUploadMemoryBarrier(); @@ -1076,27 +1157,22 @@ void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) const GPUVAddr gpu_addr = image.gpu_addr; if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { - gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), image.guest_size_bytes); + gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(), + VideoCommon::CacheType::NoTextureCache); const auto uploads = FullUploadSwizzles(image.info); - runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads)); + runtime.AccelerateImageUpload(image, staging, uploads, 0, 0); return; } Tegra::Memory::GpuGuestMemory swizzle_data( *gpu_memory, gpu_addr, image.guest_size_bytes, &swizzle_data_buffer); if (True(image.flags & ImageFlagBits::Converted)) { - if (CanConvertFromGuest(image.info)) { - const auto copies = - FixSmallVectorADL(ConvertImageFromGuest(swizzle_data, image.info, mapped_span)); - image.UploadMemory(staging, copies); - return; - } unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); - auto copies = FixSmallVectorADL(UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, unswizzle_data_buffer)); + auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, unswizzle_data_buffer); ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies); image.UploadMemory(staging, copies); } else { - const auto copies = FixSmallVectorADL(UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, mapped_span)); + const auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, mapped_span); image.UploadMemory(staging, copies); } } @@ -1243,6 +1319,9 @@ void TextureCache

::InvalidateScale(Image& image) { for (size_t c : active_channel_ids) { auto& channel_info = channel_storage[c]; + if constexpr (ENABLE_VALIDATION) + for (auto& e : channel_info.image_view_ids) + e.second = CORRUPT_ID; channel_info.graphics_image_table.Invalidate(); channel_info.compute_image_table.Invalidate(); } @@ -1265,33 +1344,18 @@ u64 TextureCache

::GetScaledImageSizeBytes(const ImageBase& image) { template void TextureCache

::QueueAsyncDecode(Image& image, ImageId image_id) { UNIMPLEMENTED_IF(False(image.flags & ImageFlagBits::Converted)); + LOG_INFO(HW_GPU, "Queuing async texture decode"); + image.flags |= ImageFlagBits::IsDecoding; auto decode = std::make_unique(); auto* decode_ptr = decode.get(); decode->image_id = image_id; async_decodes.push_back(std::move(decode)); - const size_t out_size = MapSizeBytes(image); - if (CanConvertFromGuest(image.info)) { - decode_ptr->input_data.resize_destructive(image.guest_size_bytes); - gpu_memory->ReadBlockUnsafe(image.gpu_addr, decode_ptr->input_data.data(), - image.guest_size_bytes); - - texture_decode_worker.QueueWork([out_size, info = image.info, async_decode = decode_ptr] { - async_decode->decoded_data.resize_destructive(out_size); - auto copies = - ConvertImageFromGuest(async_decode->input_data, info, async_decode->decoded_data); - - std::unique_lock lock{async_decode->mutex}; - async_decode->copies = std::move(copies); - async_decode->complete = true; - }); - return; - } - std::vector local_unswizzle_data_buffer(image.unswizzled_size_bytes, 0); Tegra::Memory::GpuGuestMemory swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer); auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer); + const size_t out_size = MapSizeBytes(image); auto func = [out_size, copies, info = image.info, input = std::move(local_unswizzle_data_buffer), @@ -1308,6 +1372,20 @@ void TextureCache

::QueueAsyncDecode(Image& image, ImageId image_id) { texture_decode_worker.QueueWork(std::move(func)); } +template +void TextureCache

::QueueAsyncUnswizzle(Image& image, ImageId image_id) { + if (True(image.flags & ImageFlagBits::IsDecoding)) { + return; + } + + image.flags |= ImageFlagBits::IsDecoding; + + unswizzle_queue.push_back({ + .image_id = image_id, + .info = image.info + }); +} + template void TextureCache

::TickAsyncDecode() { bool has_uploads{}; @@ -1323,7 +1401,7 @@ void TextureCache

::TickAsyncDecode() { auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); std::memcpy(staging.mapped_span.data(), async_decode->decoded_data.data(), async_decode->decoded_data.size()); - image.UploadMemory(staging, FixSmallVectorADL(async_decode->copies)); + image.UploadMemory(staging, async_decode->copies); image.flags &= ~ImageFlagBits::IsDecoding; has_uploads = true; i = async_decodes.erase(i); @@ -1333,6 +1411,83 @@ void TextureCache

::TickAsyncDecode() { } } +template +void TextureCache

::TickAsyncUnswizzle() { + if (unswizzle_queue.empty()) { + return; + } + + if(current_unswizzle_frame > 0) { + current_unswizzle_frame--; + return; + } + + PendingUnswizzle& task = unswizzle_queue.front(); + Image& image = slot_images[task.image_id]; + + if (!task.initialized) { + task.total_size = MapSizeBytes(image); + task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true); + + const auto& info = image.info; + const u32 bytes_per_block = BytesPerBlock(info.format); + const u32 width_blocks = Common::DivCeil(info.size.width, 4u); + const u32 height_blocks = Common::DivCeil(info.size.height, 4u); + + const u32 stride = width_blocks * bytes_per_block; + const u32 aligned_height = height_blocks; + task.bytes_per_slice = static_cast(stride) * aligned_height; + task.last_submitted_offset = 0; + task.initialized = true; + } + + // Read data + if (task.current_offset < task.total_size) { + const size_t remaining = task.total_size - task.current_offset; + + size_t copy_amount = (std::min)(swizzle_chunk_size, remaining); + + if (remaining > swizzle_chunk_size) { + copy_amount = (copy_amount / task.bytes_per_slice) * task.bytes_per_slice; + if (copy_amount == 0) copy_amount = task.bytes_per_slice; + } + + gpu_memory->ReadBlock(image.gpu_addr + task.current_offset, + task.staging_buffer.mapped_span.data() + task.current_offset, + copy_amount); + task.current_offset += copy_amount; + } + + const bool is_final_batch = task.current_offset >= task.total_size; + const size_t bytes_ready = task.current_offset - task.last_submitted_offset; + const u32 complete_slices = static_cast(bytes_ready / task.bytes_per_slice); + + if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) { + const u32 z_start = static_cast(task.last_submitted_offset / task.bytes_per_slice); + const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch); + const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start); + + if (z_count > 0) { + const auto uploads = FullUploadSwizzles(task.info); + runtime.AccelerateImageUpload(image, task.staging_buffer, uploads, z_start, z_count); + task.last_submitted_offset += (static_cast(z_count) * task.bytes_per_slice); + } + } + + // Check if complete + const u32 slices_submitted = static_cast(task.last_submitted_offset / task.bytes_per_slice); + const bool all_slices_submitted = slices_submitted >= image.info.size.depth; + + if (is_final_batch && all_slices_submitted) { + runtime.FreeDeferredStagingBuffer(task.staging_buffer); + image.flags &= ~ImageFlagBits::IsDecoding; + unswizzle_queue.pop_front(); + + // Wait 4 frames to process the next entry + current_unswizzle_frame = 4u; + } +} + template bool TextureCache

::ScaleUp(Image& image) { const bool has_copy = image.HasScaled(); @@ -1482,6 +1637,8 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); Image& new_image = slot_images[new_image_id]; + new_image.allocation_tick = frame_tick; + if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes) && new_info.is_sparse) { new_image.flags |= ImageFlagBits::Sparse; @@ -1499,9 +1656,8 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA DeleteImage(overlap_id); } - new_image.flags |= ImageFlagBits::ReorderableUpload; + // TODO: Only upload what we need RefreshContents(new_image, new_image_id); - new_image.flags &= ~ImageFlagBits::ReorderableUpload; if (can_rescale) { ScaleUp(new_image); @@ -1574,9 +1730,9 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA const u32 down_shift = can_rescale ? resolution.down_shift : 0; auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift); if (overlap.info.num_samples != new_image.info.num_samples) { - runtime.CopyImageMSAA(new_image, overlap, FixSmallVectorADL(copies)); + runtime.CopyImageMSAA(new_image, overlap, copies); } else { - runtime.CopyImage(new_image, overlap, FixSmallVectorADL(copies)); + runtime.CopyImage(new_image, overlap, copies); } new_image.modification_tick = overlap.modification_tick; } @@ -2240,6 +2396,9 @@ void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { } for (size_t c : active_channel_ids) { auto& channel_info = channel_storage[c]; + if constexpr (ENABLE_VALIDATION) + for (auto& e : channel_info.image_view_ids) + e.second = CORRUPT_ID; channel_info.graphics_image_table.Invalidate(); channel_info.compute_image_table.Invalidate(); } @@ -2264,6 +2423,7 @@ void TextureCache

::RemoveImageViewReferences(std::span rem template void TextureCache

::RemoveFramebuffers(std::span removed_views) { + runtime.FlushDeferredClear(); auto it = framebuffers.begin(); while (it != framebuffers.end()) { if (it->first.Contains(removed_views)) { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 0243a68a2d..8a2318cc72 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -31,7 +31,6 @@ #include "common/thread_worker.h" #include "video_core/compatible_formats.h" #include "video_core/control/channel_state_cache.h" -#include "video_core/cache_reclaim.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/engines/fermi_2d.h" #include "video_core/surface.h" @@ -64,7 +63,6 @@ struct ImageViewInOut { struct AsyncDecodeContext { ImageId image_id; - Common::ScratchBuffer input_data; Common::ScratchBuffer decoded_data; boost::container::small_vector copies; std::mutex mutex; @@ -89,6 +87,9 @@ public: std::unordered_map image_views; std::unordered_map samplers; + ::Common::unordered_map sampler_ids; + ::Common::unordered_map image_view_ids; + TextureCacheGPUMap* gpu_page_table = nullptr; TextureCacheGPUMap* sparse_page_table = nullptr; }; @@ -98,6 +99,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches void UploadImageContents(Image& image, StagingBuffer& staging_buffer); @@ -411,6 +422,9 @@ private: void QueueAsyncDecode(Image& image, ImageId image_id); void TickAsyncDecode(); + void QueueAsyncUnswizzle(Image& image, ImageId image_id); + void TickAsyncUnswizzle(); + Runtime& runtime; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -418,7 +432,13 @@ private: RenderTargets render_targets; u64 render_targets_serial = 0; + u32 rt_active_mask = 0; + std::array rt_image_id{}; ImageId rt_depth_image_id{}; + u64 texture_bindings_serial = 0; + u64 last_feedback_loop_serial = 0; + u64 last_feedback_texture_serial = 0; + bool last_feedback_loop_result = false; FramebufferId last_framebuffer_id{}; u64 last_framebuffer_serial = 0; @@ -431,12 +451,12 @@ private: bool has_deleted_images = false; bool is_rescaling = false; u64 total_used_memory = 0; - u64 device_local_memory = 0; - u64 minimum_memory = 0; - u64 expected_memory = 0; - u64 critical_memory = 0; - u64 heap_headroom = 0; - bool heap_pressure = false; + u64 minimum_memory; + u64 expected_memory; + u64 critical_memory; + size_t gpu_unswizzle_maxsize = 0; + size_t swizzle_chunk_size = 0; + u32 swizzle_slices_per_batch = 0; struct BufferDownload { GPUVAddr address; @@ -488,9 +508,13 @@ private: u64 modification_tick = 0; u64 frame_tick = 0; - Common::ThreadWorker texture_decode_worker{1, "TextureDecoder"}; + Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {}, + Common::ThreadPlacement::Efficiency}; std::vector> async_decodes; + std::deque unswizzle_queue; + u8 current_unswizzle_frame; + // Join caching boost::container::small_vector join_overlap_ids; ::Common::unordered_set join_overlaps_found; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index d8cae4f3ee..e55d0752ec 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -22,6 +22,7 @@ #include "common/common_types.h" #include "common/div_ceil.h" #include "common/scratch_buffer.h" +#include "common/settings.h" #include "video_core/compatible_formats.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/guest_memory.h" @@ -33,6 +34,7 @@ #include "video_core/texture_cache/samples_helper.h" #include "video_core/texture_cache/util.h" #include "video_core/textures/astc.h" +#include "video_core/textures/bcn.h" #include "video_core/textures/decoders.h" namespace VideoCommon { @@ -606,6 +608,21 @@ u32 CalculateConvertedSizeBytes(const ImageInfo& info) noexcept { return info.size.width * BytesPerBlock(info.format); } static constexpr Extent2D TILE_SIZE{1, 1}; + if (IsPixelFormatASTC(info.format) && Settings::values.astc_recompression.GetValue() != + Settings::AstcRecompression::Uncompressed) { + const u32 bpp_div = + Settings::values.astc_recompression.GetValue() == Settings::AstcRecompression::Bc1 ? 2 + : 1; + // NumBlocksPerLayer doesn't account for this correctly, so we have to do it manually. + u32 output_size = 0; + for (s32 i = 0; i < info.resources.levels; i++) { + const auto mip_size = AdjustMipSize(info.size, i); + const u32 plane_dim = + Common::AlignUp(mip_size.width, 4U) * Common::AlignUp(mip_size.height, 4U); + output_size += (plane_dim * info.size.depth * info.resources.layers) / bpp_div; + } + return output_size; + } return NumBlocksPerLayer(info, TILE_SIZE) * info.resources.layers * ConvertedBytesPerBlock(info.format); } @@ -908,6 +925,7 @@ boost::container::small_vector UnswizzleImage(Tegra::Memory void ConvertImage(std::span input, const ImageInfo& info, std::span output, std::span copies) { u32 output_offset = 0; + Common::ScratchBuffer decode_scratch; const Extent2D tile_size = DefaultBlockSize(info.format); for (BufferImageCopy& copy : copies) { @@ -922,19 +940,52 @@ void ConvertImage(std::span input, const ImageInfo& info, std::span(copy.buffer_size); } else { DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format); output_offset += copy.image_extent.width * copy.image_extent.height * - copy.image_extent.depth * copy.image_subresource.num_layers * + copy.image_subresource.num_layers * ConvertedBytesPerBlock(info.format); } @@ -943,74 +994,6 @@ void ConvertImage(std::span input, const ImageInfo& info, std::span ConvertImageFromGuest( - std::span input, const ImageInfo& info, std::span output) { - const u32 bpp_log2 = BytesPerBlockLog2(info.format); - const Extent2D tile_size = DefaultBlockSize(info.format); - const Extent3D size = info.size; - const LevelInfo level_info = MakeLevelInfo(info); - const s32 num_layers = info.resources.layers; - const s32 num_levels = info.resources.levels; - const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); - const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); - const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels); - const u32 layer_stride = AlignLayerSize(layer_size, size, level_info.block, tile_size.height, - info.tile_width_spacing); - const u32 out_bytes_per_texel = BytesPerBlock(PixelFormat::A8B8G8R8_UNORM); - size_t guest_offset = 0; - u32 output_offset = 0; - boost::container::small_vector copies(num_levels); - - for (s32 level = 0; level < num_levels; ++level) { - const Extent3D level_size = AdjustMipSize(size, level); - const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - const Extent3D block = - AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); - const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2); - const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) << bpp_log2; - const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); - const u32 gob_block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth); - const u32 level_bytes = level_size.width * level_size.height * level_size.depth * - num_layers * out_bytes_per_texel; - copies[level] = BufferImageCopy{ - .buffer_offset = output_offset, - .buffer_size = level_bytes, - .buffer_row_length = level_size.width, - .buffer_image_height = level_size.height, - .image_subresource = - { - .base_level = level, - .base_layer = 0, - .num_layers = num_layers, - }, - .image_offset = {0, 0, 0}, - .image_extent = level_size, - }; - const Tegra::Texture::ASTC::BlockLinearLayout layout{ - .layer_stride = layer_stride, - .slice_size = - Common::DivCeilLog2(num_tiles.height, block.height + GOB_SIZE_Y_SHIFT) * - gob_block_size, - .block_size = gob_block_size, - .x_shift = GOB_SIZE_SHIFT + block.height + block.depth, - .gob_height = block.height, - .gob_height_mask = (1U << block.height) - 1, - .gob_depth = block.depth, - .gob_depth_mask = (1U << block.depth) - 1, - }; - Tegra::Texture::ASTC::DecompressBlockLinear( - input.subspan(guest_offset), level_size.width, level_size.height, level_size.depth, - num_layers, tile_size.width, tile_size.height, layout, output.subspan(output_offset)); - output_offset += level_bytes; - guest_offset += level_sizes[level]; - } - return copies; -} - boost::container::small_vector FullDownloadCopies(const ImageInfo& info) { const Extent3D size = info.size; const u32 bytes_per_block = BytesPerBlock(info.format); diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 1bc4b53582..590d85b042 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -72,11 +72,6 @@ struct OverlapResult { void ConvertImage(std::span input, const ImageInfo& info, std::span output, std::span copies); -[[nodiscard]] bool CanConvertFromGuest(const ImageInfo& info); - -[[nodiscard]] boost::container::small_vector ConvertImageFromGuest( - std::span input, const ImageInfo& info, std::span output); - [[nodiscard]] boost::container::small_vector FullDownloadCopies( const ImageInfo& info); @@ -127,25 +122,4 @@ void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* [[nodiscard]] u32 MapSizeBytes(const ImageBase& image); -// TODO: Remove once Debian STABLE no longer has such outdated boost -// This is a gcc bug where ADL lookup fails for range niebloids of std::span -// for any given type of the static_vector/small_vector, etc which makes a whole mess -// for anything using std::span so we just do this terrible hack on older versions of -// GCC12 because people actually still use stable debian so... yeah -// One may say: "This is bad for performance" - to which I say, using GCC 12 you already know -// what kind of bs you will be dealing with anyways. -template -#if BOOST_VERSION >= 108100 || __GNUC__ > 12 -[[nodiscard]] boost::container::small_vector FixSmallVectorADL(const boost::container::small_vector& v) { - return v; -} -#else -[[nodiscard]] std::vector FixSmallVectorADL(const boost::container::small_vector& v) { - std::vector u; - for (auto const& e : v) - u.push_back(e); - return u; -} -#endif - } // namespace VideoCommon diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 678e52a242..0b6d06c49a 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -20,7 +20,6 @@ #include "common/common_types.h" #include #include "video_core/textures/astc.h" -#include "video_core/textures/decoders.h" #include "video_core/textures/workers.h" class InputBitStream { @@ -1672,7 +1671,7 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues, } static void FillVoidExtentLDR(InputBitStream& strm, std::span outBuf, u32 blockWidth, - u32 blockHeight, u32 stride) { + u32 blockHeight) { // Don't actually care about the void extent, just read the bits... for (s32 i = 0; i < 4; ++i) { strm.ReadBits<13>(); @@ -1689,7 +1688,7 @@ static void FillVoidExtentLDR(InputBitStream& strm, std::span outBuf, u32 b for (u32 j = 0; j < blockHeight; j++) { for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * stride + i] = rgba; + outBuf[j * blockWidth + i] = rgba; } } } @@ -1727,52 +1726,52 @@ static u16 HalfToClampedByte(u16 half_bits) { return static_cast(clamped * 255.0f + 0.5f); } -static void FillError(std::span outBuf, u32 blockWidth, u32 blockHeight, u32 stride) { +static void FillError(std::span outBuf, u32 blockWidth, u32 blockHeight) { for (u32 j = 0; j < blockHeight; j++) { for (u32 i = 0; i < blockWidth; i++) { - outBuf[j * stride + i] = 0x00000000; + outBuf[j * blockWidth + i] = 0x00000000; } } } static void DecompressBlock(std::span inBuf, const u32 blockWidth, - const u32 blockHeight, std::span outBuf, u32 stride) { + const u32 blockHeight, std::span outBuf) { InputBitStream strm(inBuf); TexelWeightParams weightParams = DecodeBlockInfo(strm); // Was there an error? if (weightParams.m_bError) { assert(false && "Invalid block mode"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } if (weightParams.m_bVoidExtentLDR) { - FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight, stride); + FillVoidExtentLDR(strm, outBuf, blockWidth, blockHeight); return; } if (weightParams.m_bVoidExtentHDR) { assert(false && "HDR void extent blocks are unsupported!"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } if (weightParams.m_Width > blockWidth) { assert(false && "Texel weight grid width should be smaller than block width"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } if (weightParams.m_Height > blockHeight) { assert(false && "Texel weight grid height should be smaller than block height"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } if (weightParams.GetNumWeightValues() > 64) { assert(false && "Too many weights in the weight grid"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } @@ -1782,7 +1781,7 @@ static void DecompressBlock(std::span inBuf, const u32 blockWidth, if (nPartitions == 4 && weightParams.m_bDualPlane) { assert(false && "Dual plane mode is incompatible with four partition blocks"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } @@ -1814,7 +1813,7 @@ static void DecompressBlock(std::span inBuf, const u32 blockWidth, u32 nWeightBits = weightParams.GetPackedBitSize(); if (nWeightBits < 24 || nWeightBits > 96) { assert(false && "Invalid weight bit count"); - FillError(outBuf, blockWidth, blockHeight, stride); + FillError(outBuf, blockWidth, blockHeight); return; } s32 remainingBits = 128 - nWeightBits - static_cast(strm.GetBitsRead()); @@ -1996,54 +1995,41 @@ static void DecompressBlock(std::span inBuf, const u32 blockWidth, } } - outBuf[j * stride + i] = p.Pack(); + outBuf[j * blockWidth + i] = p.Pack(); } } -static void DecodeAndStoreBlock(std::span data, size_t src_offset, u32 x, u32 y, - u32 width, u32 height, u32 block_width, u32 block_height, - std::span out_slice) { - const u32 decomp_width = (std::min)(block_width, width - x); - const u32 decomp_height = (std::min)(block_height, height - y); - u32* const dst = out_slice.data() + size_t{y} * width + x; - if (src_offset + 16 > data.size()) { - for (u32 h = 0; h < decomp_height; ++h) { - std::memset(dst + size_t{h} * width, 0, decomp_width * 4); - } - return; - } - const std::span block_ptr{data.subspan(src_offset, 16)}; - if (decomp_width == block_width && decomp_height == block_height) { - const size_t touched = size_t{block_height - 1} * width + block_width; - DecompressBlock(block_ptr, block_width, block_height, std::span{dst, touched}, width); - return; - } - std::array staging; - DecompressBlock(block_ptr, block_width, block_height, staging, block_width); - for (u32 h = 0; h < decomp_height; ++h) { - std::memcpy(dst + size_t{h} * width, staging.data() + h * block_width, decomp_width * 4); - } -} - void Decompress(std::span data, uint32_t width, uint32_t height, uint32_t depth, uint32_t block_width, uint32_t block_height, std::span output) { const u32 rows = Common::DivideUp(height, block_height); const u32 cols = Common::DivideUp(width, block_width); Common::ThreadWorker& workers{GetThreadWorkers()}; - const std::span out{reinterpret_cast(output.data()), output.size() / 4}; - const size_t slice_texels = size_t{height} * width; for (u32 z = 0; z < depth; ++z) { - const std::span out_slice = out.subspan(z * slice_texels); + const u32 depth_offset = z * height * width * 4; for (u32 y_index = 0; y_index < rows; ++y_index) { - auto decompress_stride = [data, width, height, block_width, block_height, out_slice, - rows, cols, z, y_index] { + auto decompress_stride = [data, width, height, block_width, block_height, output, rows, + cols, z, depth_offset, y_index] { const u32 y = y_index * block_height; for (u32 x_index = 0; x_index < cols; ++x_index) { const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; - DecodeAndStoreBlock(data, size_t{block_index} * 16, x_index * block_width, y, - width, height, block_width, block_height, out_slice); + const u32 x = x_index * block_width; + + const std::span blockPtr{data.subspan(block_index * 16, 16)}; + + // Blocks can be at most 12x12 + std::array uncompData; + DecompressBlock(blockPtr, block_width, block_height, uncompData); + + u32 decompWidth = (std::min)(block_width, width - x); + u32 decompHeight = (std::min)(block_height, height - y); + + const std::span outRow = output.subspan(depth_offset + (y * width + x) * 4); + for (u32 h = 0; h < decompHeight; ++h) { + std::memcpy(outRow.data() + h * width * 4, + uncompData.data() + h * block_width, decompWidth * 4); + } } }; workers.QueueWork(std::move(decompress_stride)); @@ -2052,50 +2038,4 @@ void Decompress(std::span data, uint32_t width, uint32_t height, } } -void DecompressBlockLinear(std::span data, uint32_t width, uint32_t height, - uint32_t depth, uint32_t layers, uint32_t block_width, - uint32_t block_height, const BlockLinearLayout& layout, - std::span output) { - const u32 rows = Common::DivideUp(height, block_height); - const u32 cols = Common::DivideUp(width, block_width); - const size_t slice_texels = size_t{height} * width; - - Common::ThreadWorker& workers{GetThreadWorkers()}; - const std::span out{reinterpret_cast(output.data()), output.size() / 4}; - - for (u32 layer = 0; layer < layers; ++layer) { - const size_t layer_offset = size_t{layer} * layout.layer_stride; - for (u32 z = 0; z < depth; ++z) { - const size_t slice_offset = - layer_offset + (z >> layout.gob_depth) * size_t{layout.slice_size} + - ((z & layout.gob_depth_mask) << (GOB_SIZE_SHIFT + layout.gob_height)); - const std::span out_slice = - out.subspan((size_t{layer} * depth + z) * slice_texels); - for (u32 y_index = 0; y_index < rows; ++y_index) { - auto decompress_stride = [data, width, height, block_width, block_height, out_slice, - cols, layout, slice_offset, y_index] { - const u32 y = y_index * block_height; - const u32 gob_y = y_index >> GOB_SIZE_Y_SHIFT; - const size_t offset_y = - (gob_y >> layout.gob_height) * size_t{layout.block_size} + - ((gob_y & layout.gob_height_mask) << GOB_SIZE_SHIFT); - const u32 swizzled_y = ((y_index & 1) << 4) | ((y_index & 6) << 5); - for (u32 x_index = 0; x_index < cols; ++x_index) { - const u32 byte_x = x_index * 16; - const size_t offset_x = size_t{byte_x >> GOB_SIZE_X_SHIFT} - << layout.x_shift; - const u32 swizzled_x = ((x_index & 1) << 5) | ((x_index & 2) << 7); - const size_t src_offset = slice_offset + offset_y + offset_x + - (swizzled_x | swizzled_y); - DecodeAndStoreBlock(data, src_offset, x_index * block_width, y, width, - height, block_width, block_height, out_slice); - } - }; - workers.QueueWork(std::move(decompress_stride)); - } - } - } - workers.WaitForRequests(); -} - } // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h index 5d59da7496..afd3933c3e 100644 --- a/src/video_core/textures/astc.h +++ b/src/video_core/textures/astc.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,23 +5,7 @@ namespace Tegra::Texture::ASTC { -struct BlockLinearLayout { - uint32_t layer_stride; - uint32_t slice_size; - uint32_t block_size; - uint32_t x_shift; - uint32_t gob_height; - uint32_t gob_height_mask; - uint32_t gob_depth; - uint32_t gob_depth_mask; -}; - void Decompress(std::span data, uint32_t width, uint32_t height, uint32_t depth, uint32_t block_width, uint32_t block_height, std::span output); -void DecompressBlockLinear(std::span data, uint32_t width, uint32_t height, - uint32_t depth, uint32_t layers, uint32_t block_width, - uint32_t block_height, const BlockLinearLayout& layout, - std::span output); - } // namespace Tegra::Texture::ASTC diff --git a/src/video_core/textures/bcn.cpp b/src/video_core/textures/bcn.cpp new file mode 100644 index 0000000000..16ddbe3202 --- /dev/null +++ b/src/video_core/textures/bcn.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "common/alignment.h" +#include "video_core/textures/bcn.h" +#include "video_core/textures/workers.h" + +namespace Tegra::Texture::BCN { + +using BCNCompressor = void(u8* block_output, const u8* block_input, bool any_alpha); + +template +void CompressBCN(std::span data, uint32_t width, uint32_t height, uint32_t depth, + std::span output, BCNCompressor f) { + constexpr u8 alpha_threshold = 128; + constexpr u32 bytes_per_px = 4; + const u32 plane_dim = width * height; + + Common::ThreadWorker& workers{GetThreadWorkers()}; + + for (u32 z = 0; z < depth; z++) { + for (u32 y = 0; y < height; y += 4) { + auto compress_row = [z, y, width, height, plane_dim, f, data, output]() { + for (u32 x = 0; x < width; x += 4) { + // Gather 4x4 block of RGBA texels + u8 input_colors[4][4][4]; + bool any_alpha = false; + + for (u32 j = 0; j < 4; j++) { + for (u32 i = 0; i < 4; i++) { + const size_t coord = + (z * plane_dim + (y + j) * width + (x + i)) * bytes_per_px; + + if ((x + i < width) && (y + j < height)) { + if constexpr (ThresholdAlpha) { + if (data[coord + 3] >= alpha_threshold) { + input_colors[j][i][0] = data[coord + 0]; + input_colors[j][i][1] = data[coord + 1]; + input_colors[j][i][2] = data[coord + 2]; + input_colors[j][i][3] = 255; + } else { + any_alpha = true; + memset(input_colors[j][i], 0, bytes_per_px); + } + } else { + memcpy(input_colors[j][i], &data[coord], bytes_per_px); + } + } else { + memset(input_colors[j][i], 0, bytes_per_px); + } + } + } + + const u32 bytes_per_row = BytesPerBlock * Common::DivideUp(width, 4U); + const u32 bytes_per_plane = bytes_per_row * Common::DivideUp(height, 4U); + f(output.data() + z * bytes_per_plane + (y / 4) * bytes_per_row + + (x / 4) * BytesPerBlock, + reinterpret_cast(input_colors), any_alpha); + } + }; + workers.QueueWork(std::move(compress_row)); + } + workers.WaitForRequests(); + } +} + +void CompressBC1(std::span data, uint32_t width, uint32_t height, uint32_t depth, + std::span output) { + CompressBCN<8, true>(data, width, height, depth, output, + [](u8* block_output, const u8* block_input, bool any_alpha) { + stb_compress_bc1_block(block_output, block_input, any_alpha, + STB_DXT_NORMAL); + }); +} + +void CompressBC3(std::span data, uint32_t width, uint32_t height, uint32_t depth, + std::span output) { + CompressBCN<16, false>(data, width, height, depth, output, + [](u8* block_output, const u8* block_input, bool any_alpha) { + stb_compress_bc3_block(block_output, block_input, STB_DXT_NORMAL); + }); +} + +} // namespace Tegra::Texture::BCN diff --git a/src/video_core/textures/bcn.h b/src/video_core/textures/bcn.h new file mode 100644 index 0000000000..d5d2a16c91 --- /dev/null +++ b/src/video_core/textures/bcn.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Tegra::Texture::BCN { + +void CompressBC1(std::span data, u32 width, u32 height, u32 depth, std::span output); + +void CompressBC3(std::span data, u32 width, u32 height, u32 depth, std::span output); + +} // namespace Tegra::Texture::BCN diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 29d44b77db..25b0860b0f 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -41,9 +41,9 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 u32 block_height, u32 block_depth, u32 stride) { // The origin of the transformation can be configured here, leave it as zero as the current API // doesn't expose it. + static constexpr u32 origin_x = 0; static constexpr u32 origin_y = 0; static constexpr u32 origin_z = 0; - static constexpr u32 columns_per_gob = GOB_SIZE_X / BYTES_PER_PIXEL; // We can configure here a custom pitch // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch. @@ -62,38 +62,30 @@ void SwizzleImpl(std::span output, std::span input, u32 width, u32 const u32 z = slice + origin_z; const u32 offset_z = (z >> block_depth) * slice_size + ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); - const u32 slice_base = slice * pitch * height; - for (u32 band = 0; band < height; band += GOB_SIZE_Y) { - const u32 band_end = (std::min)(band + GOB_SIZE_Y, height); - const u32 block_y = (band + origin_y) >> GOB_SIZE_Y_SHIFT; + for (u32 line = 0; line < height; ++line) { + const u32 y = line + origin_y; + const u32 swizzled_y = pdep(y); + + const u32 block_y = y >> GOB_SIZE_Y_SHIFT; const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - const u32 band_base = offset_z + offset_y; - u32 band_linear = slice_base + band * pitch; - u32 offset_x = 0; - for (u32 gob = 0; gob < width; gob += columns_per_gob, - offset_x += 1U << x_shift, band_linear += GOB_SIZE_X) { - const u32 columns = (std::min)(columns_per_gob, width - gob); - const u32 gob_base = band_base + offset_x; - u32 linear_row = band_linear; - for (u32 line = band; line < band_end; ++line, linear_row += pitch) { - const u32 y = line + origin_y; - const u32 row_base = gob_base + ((y & 1) << 4) + ((y & 6) << 5); + u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); + for (u32 column = 0; column < width; + ++column, incrpdep(swizzled_x)) { + const u32 x = (column + origin_x) * BYTES_PER_PIXEL; + const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; - u32 linear_offset = linear_row; - u32 swizzled_x = 0; - for (u32 column = 0; column < columns; - ++column, incrpdep(swizzled_x), - linear_offset += BYTES_PER_PIXEL) { - const u32 swizzled_offset = row_base + swizzled_x; + const u32 base_swizzled_offset = offset_z + offset_y + offset_x; + const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); - u8* const dst = &output[TO_LINEAR ? swizzled_offset : linear_offset]; - const u8* const src = &input[TO_LINEAR ? linear_offset : swizzled_offset]; + const u32 unswizzled_offset = + slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; - std::memcpy(dst, src, BYTES_PER_PIXEL); - } - } + u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; + const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; + + std::memcpy(dst, src, BYTES_PER_PIXEL); } } } @@ -131,25 +123,26 @@ void SwizzleSubrectImpl(std::span output, std::span input, u32 wid const u32 lines_in_y = (std::min)(unprocessed_lines, extent_y); for (u32 line = 0; line < lines_in_y; ++line) { const u32 y = line + origin_y; - const u32 swizzled_y = ((y & 1) << 4) | ((y & 6) << 5); + const u32 swizzled_y = pdep(y); const u32 block_y = y >> GOB_SIZE_Y_SHIFT; const u32 offset_y = (block_y >> block_height) * block_size + ((block_y & block_height_mask) << GOB_SIZE_SHIFT); - const u32 row_base = offset_z + offset_y + swizzled_y; - u32 linear_offset = slice * pitch * height + line * pitch; u32 swizzled_x = pdep(origin_x * BYTES_PER_PIXEL); for (u32 column = 0; column < extent_x; - ++column, incrpdep(swizzled_x), - linear_offset += BYTES_PER_PIXEL) { + ++column, incrpdep(swizzled_x)) { const u32 x = (column + origin_x) * BYTES_PER_PIXEL; const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; - const u32 swizzled_offset = row_base + offset_x + swizzled_x; + const u32 base_swizzled_offset = offset_z + offset_y + offset_x; + const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); - u8* const dst = &output[TO_LINEAR ? swizzled_offset : linear_offset]; - const u8* const src = &input[TO_LINEAR ? linear_offset : swizzled_offset]; + const u32 unswizzled_offset = + slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; + + u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; + const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; std::memcpy(dst, src, BYTES_PER_PIXEL); } @@ -171,8 +164,11 @@ void Swizzle(std::span output, std::span input, u32 bytes_per_pixe block_depth, stride_alignment); BPP_CASE(1) BPP_CASE(2) + BPP_CASE(3) BPP_CASE(4) + BPP_CASE(6) BPP_CASE(8) + BPP_CASE(12) BPP_CASE(16) #undef BPP_CASE default: diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 8018f444be..1e14a38753 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -24,6 +24,7 @@ constexpr u32 GOB_SIZE_Z_SHIFT = 0; constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; constexpr u32 SWIZZLE_X_BITS = 0b100101111; +constexpr u32 SWIZZLE_Y_BITS = 0b011010000; /// Unswizzles a block linear texture into linear memory. void UnswizzleTexture(std::span output, std::span input, u32 bytes_per_pixel, diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index cd5e5a8412..be09bca8d3 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include "common/container/unordered_map.h" @@ -17,6 +19,8 @@ #include #include "common/assert.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/literals.h" #include #include "common/settings.h" @@ -301,6 +305,10 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, + VK_FORMAT_EAC_R11_UNORM_BLOCK, + VK_FORMAT_EAC_R11_SNORM_BLOCK, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK, }; ::Common::unordered_map format_properties; for (const auto format : formats) { @@ -390,6 +398,17 @@ std::vector ExtensionListForVulkan( return output; } +constexpr std::array STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'}; +constexpr u32 STATIC_CACHE_VERSION = 1; + +std::filesystem::path StaticPipelineCacheFilename() { + const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + if (!Common::FS::CreateDir(shader_dir)) { + return {}; + } + return shader_dir / "vulkan_static_pipelines.bin"; +} + } // Anonymous namespace void Device::RemoveExtension(bool& extension, const std::string& extension_name) { @@ -433,13 +452,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const vk::InstanceDispatch& dld_) : instance{instance_}, dld{dld_}, physical{physical_}, format_properties(GetFormatProperties(physical)) { - const VkPhysicalDeviceMemoryProperties memory_properties = - physical.GetMemoryProperties().memoryProperties; - is_uma = std::all_of(memory_properties.memoryTypes, - memory_properties.memoryTypes + memory_properties.memoryTypeCount, - [](const VkMemoryType& type) { - return (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0; - }); // Get suitability and device properties. const bool is_suitable = GetSuitability(surface != VkSurfaceKHR{}); @@ -494,6 +506,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize; is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; + is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || + properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; supports_d24_depth = IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, @@ -571,7 +586,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const auto arch = GetNvidiaArch(); if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) { LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); - has_broken_float16_math = true; + features.shader_float16_int8.shaderFloat16 = false; } // Use hardware depth/stencil blits instead when available @@ -603,6 +618,13 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR "AMD GCN4 and earlier have broken VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT"); has_broken_cube_compatibility = true; } + + // AMD drivers (2026+) have broken float16 math on DKCR + if (features.shader_float16_int8.shaderFloat16) { + LOG_WARNING(Render_Vulkan, + "AMD drivers (2026+) have broken float16 math"); + features.shader_float16_int8.shaderFloat16 = false; + } } if (extensions.sampler_filter_minmax && is_amd) { @@ -738,21 +760,106 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR .pHeapSizeLimit = nullptr, .pVulkanFunctions = &functions, .instance = instance, - .vulkanApiVersion = VK_API_VERSION_1_1, + .vulkanApiVersion = ApiVersion(), .pTypeExternalMemoryHandleTypes = nullptr, }; vk::Check(vmaCreateAllocator(&allocator_info, &allocator)); + owns_static_pipeline_cache = surface != VkSurfaceKHR{}; + LoadStaticPipelineCache(); + // Initialize GPU logging if enabled InitializeGPULogging(); } Device::~Device() { + SaveStaticPipelineCache(); ShutdownGPULogging(); vmaDestroyAllocator(allocator); } +void Device::LoadStaticPipelineCache() { + const auto create = [this](size_t size, const void* data) { + static_pipeline_cache = logical.CreatePipelineCache({ + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .initialDataSize = size, + .pInitialData = data, + }); + }; + if (!owns_static_pipeline_cache) { + create(0, nullptr); + return; + } + const auto filename = StaticPipelineCacheFilename(); + if (filename.empty()) { + create(0, nullptr); + return; + } + std::vector data; + try { + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + create(0, nullptr); + return; + } + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + const size_t total = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); + std::array magic{}; + u32 version{}; + if (total < magic.size() + sizeof(version)) { + create(0, nullptr); + return; + } + file.read(magic.data(), magic.size()) + .read(reinterpret_cast(&version), sizeof(version)); + if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) { + create(0, nullptr); + return; + } + data.resize(total - magic.size() - sizeof(version)); + file.read(data.data(), static_cast(data.size())); + } catch (const std::ios_base::failure& e) { + create(0, nullptr); + return; + } + create(data.size(), data.empty() ? nullptr : data.data()); +} + +void Device::SaveStaticPipelineCache() const { + if (!owns_static_pipeline_cache || !static_pipeline_cache) { + return; + } + const auto filename = StaticPipelineCacheFilename(); + if (filename.empty()) { + return; + } + size_t size = 0; + std::vector data; + static_pipeline_cache.Read(&size, nullptr); + if (size == 0) { + return; + } + data.resize(size); + static_pipeline_cache.Read(&size, data.data()); + try { + std::ofstream file(filename, std::ios::binary | std::ios::trunc); + file.exceptions(std::ofstream::failbit); + if (!file.is_open()) { + return; + } + file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size()) + .write(reinterpret_cast(&STATIC_CACHE_VERSION), + sizeof(STATIC_CACHE_VERSION)) + .write(data.data(), static_cast(size)); + } catch (const std::ios_base::failure& e) { + Common::FS::RemoveFile(filename); + } +} + VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, FormatType format_type) const { if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { @@ -893,6 +1000,10 @@ bool Device::GetSuitability(bool requires_swapchain) { // Assume we will be suitable. bool suitable = true; + // Configure properties. + VkPhysicalDeviceVulkan12Features features_1_2{}; + VkPhysicalDeviceVulkan13Features features_1_3{}; + // Configure properties. properties.properties = physical.GetProperties(); @@ -938,8 +1049,6 @@ bool Device::GetSuitability(bool requires_swapchain) { extensions.depth_stencil_resolve = extensions.depth_stencil_resolve && (instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2); - extensions.draw_indirect_count = - extensions.draw_indirect_count || instance_version >= VK_API_VERSION_1_2; RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve, VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); @@ -989,6 +1098,16 @@ bool Device::GetSuitability(bool requires_swapchain) { // Set next pointer. void** next = &features2.pNext; + // Vulkan 1.2 and 1.3 features + if (instance_version >= VK_API_VERSION_1_2) { + features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + + features_1_2.pNext = &features_1_3; + + *next = &features_1_2; + } + // Test all features we know about. If the feature is not available in core at our // current API version, and was not enabled by an extension, skip testing the feature. // We set the structure sType explicitly here as it is zeroed by the constructor. @@ -1095,16 +1214,6 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; SetNext(next, properties.subgroup_size_control); } - properties.texel_buffer_alignment.storageTexelBufferOffsetAlignmentBytes = - properties.properties.limits.minTexelBufferOffsetAlignment; - properties.texel_buffer_alignment.uniformTexelBufferOffsetAlignmentBytes = - properties.properties.limits.minTexelBufferOffsetAlignment; - if (features.texel_buffer_alignment.texelBufferAlignment == VK_TRUE || - instance_version >= VK_API_VERSION_1_3) { - properties.texel_buffer_alignment.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT; - SetNext(next, properties.texel_buffer_alignment); - } if (extensions.transform_feedback) { properties.transform_feedback.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; @@ -1121,22 +1230,11 @@ bool Device::GetSuitability(bool requires_swapchain) { SetNext(next, properties.custom_border_color); } - if (extensions.vertex_attribute_divisor) { - properties.vertex_attribute_divisor.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT; - SetNext(next, properties.vertex_attribute_divisor); - } - // Perform the property fetch. physical.GetProperties2(properties2); // Store base properties properties.properties = properties2.properties; - max_vertex_attrib_divisor = - (std::max)(1U, properties.vertex_attribute_divisor.maxVertexAttribDivisor); - if (!features.vertex_attribute_divisor.vertexAttributeInstanceRateDivisor) { - max_vertex_attrib_divisor = 1; - } // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); @@ -1215,11 +1313,6 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.color_write_enable, features.color_write_enable, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); - extensions.conditional_rendering = features.conditional_rendering.conditionalRendering; - RemoveExtensionFeatureIfUnsuitable(extensions.conditional_rendering, - features.conditional_rendering, - VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME); - // VK_EXT_custom_border_color if (extensions.custom_border_color) { extensions.custom_border_color = @@ -1255,12 +1348,6 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.descriptor_buffer, features.descriptor_buffer, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); - extensions.dynamic_rendering_unused_attachments = - features.dynamic_rendering_unused_attachments.dynamicRenderingUnusedAttachments; - RemoveExtensionFeatureIfUnsuitable(extensions.dynamic_rendering_unused_attachments, - features.dynamic_rendering_unused_attachments, - VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME); - // VK_EXT_extended_dynamic_state extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state, @@ -1413,6 +1500,18 @@ void Device::RemoveUnsuitableExtensions() { features.workgroup_memory_explicit_layout, VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); + // VK_KHR_maintenance1 + extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME); + + // VK_KHR_maintenance2 + extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME); + + // VK_KHR_maintenance3 + extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME); + // VK_KHR_maintenance4 extensions.maintenance4 = features.maintenance4.maintenance4; RemoveExtensionFeatureIfUnsuitable(extensions.maintenance4, features.maintenance4, @@ -1423,20 +1522,23 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.maintenance5, features.maintenance5, VK_KHR_MAINTENANCE_5_EXTENSION_NAME); - if (!is_uma || instance_version < VK_API_VERSION_1_3 || - !features.host_image_copy.hostImageCopy) { - RemoveExtensionFeature(extensions.host_image_copy, features.host_image_copy, - VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME); - } + // VK_KHR_maintenance6 + extensions.maintenance6 = features.maintenance6.maintenance6; + RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, + VK_KHR_MAINTENANCE_6_EXTENSION_NAME); + // VK_KHR_maintenance7 + extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); + // VK_KHR_maintenance8 + extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); + + // VK_KHR_synchronization2 extensions.synchronization2 = features.synchronization2.synchronization2; - extensions.dynamic_rendering = features.dynamic_rendering.dynamicRendering; - const bool copy_commands2 = - extensions.copy_commands2 || instance_version >= VK_API_VERSION_1_3; - if (!extensions.synchronization2 || !extensions.dynamic_rendering || !copy_commands2) { - throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); - } + RemoveExtensionFeatureIfUnsuitable(extensions.synchronization2, features.synchronization2, + VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); } void Device::SetupFamilies(VkSurfaceKHR surface) { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 63a2e5b7fa..bbf3754e39 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -6,7 +6,6 @@ #pragma once -#include #include #include #include @@ -43,7 +42,6 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, VulkanMemoryModel, VULKAN_MEMORY_MODEL, vulkan_memory_model) #define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ - FEATURE(KHR, DynamicRendering, DYNAMIC_RENDERING, dynamic_rendering) \ FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, robust_image_access) \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ shader_demote_to_helper_invocation) \ @@ -57,29 +55,24 @@ VK_DEFINE_HANDLE(VmaAllocator) #define FOR_EACH_VK_FEATURE_EXT(FEATURE) \ FEATURE(EXT, BorderColorSwizzle, BORDER_COLOR_SWIZZLE, border_color_swizzle) \ FEATURE(EXT, ColorWriteEnable, COLOR_WRITE_ENABLE, color_write_enable) \ - FEATURE(EXT, ConditionalRendering, CONDITIONAL_RENDERING, conditional_rendering) \ FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ FEATURE(EXT, DescriptorBuffer, DESCRIPTOR_BUFFER, descriptor_buffer) \ - FEATURE(EXT, DynamicRenderingUnusedAttachments, DYNAMIC_RENDERING_UNUSED_ATTACHMENTS, \ - dynamic_rendering_unused_attachments) \ FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \ FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \ FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \ FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \ - FEATURE(EXT, HostImageCopy, HOST_IMAGE_COPY, host_image_copy) \ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \ primitive_topology_list_restart) \ FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ - FEATURE(EXT, TexelBufferAlignment, TEXEL_BUFFER_ALIGNMENT, texel_buffer_alignment) \ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ - FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \ + FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ pipeline_executable_properties) \ FEATURE(KHR, ShaderQuadControl, SHADER_QUAD_CONTROL, shader_quad_control) \ @@ -90,6 +83,7 @@ VK_DEFINE_HANDLE(VmaAllocator) // Define miscellaneous extensions which may be used by the implementation here. #define FOR_EACH_VK_EXTENSION(EXTENSION) \ EXTENSION(EXT, ASTC_DECODE_MODE, astc_decode_mode) \ + EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \ EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \ EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \ EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \ @@ -98,7 +92,7 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \ EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ - EXTENSION(KHR, COPY_COMMANDS_2, copy_commands2) \ + EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ @@ -110,13 +104,17 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(KHR, SWAPCHAIN, swapchain) \ EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \ - EXTENSION(KHR, LOAD_STORE_OP_NONE, load_store_op_none) \ - EXTENSION(EXT, LOAD_STORE_OP_NONE, load_store_op_none_ext) \ + EXTENSION(KHR, MAINTENANCE_1, maintenance1) \ + EXTENSION(KHR, MAINTENANCE_2, maintenance2) \ + EXTENSION(KHR, MAINTENANCE_3, maintenance3) \ + EXTENSION(KHR, MAINTENANCE_7, maintenance7) \ + EXTENSION(KHR, MAINTENANCE_8, maintenance8) \ EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ + EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \ EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) // Define extensions which must be supported. @@ -135,6 +133,7 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ + EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ @@ -274,6 +273,10 @@ public: return physical; } + VkPipelineCache StaticPipelineCache() const noexcept { + return *static_pipeline_cache; + } + /// Returns the main graphics queue. vk::Queue GetGraphicsQueue() const { return graphics_queue; @@ -299,23 +302,6 @@ public: return properties.properties.apiVersion; } - VkDeviceSize TexelBufferAlignment(u32 texel_size) const { - VkDeviceSize texel = texel_size; - if (texel % 3 == 0) { - texel /= 3; - } - const auto& limits = properties.texel_buffer_alignment; - VkDeviceSize storage = limits.storageTexelBufferOffsetAlignmentBytes; - if (limits.storageTexelBufferOffsetSingleTexelAlignment != VK_FALSE) { - storage = (std::min)(storage, texel); - } - VkDeviceSize uniform = limits.uniformTexelBufferOffsetAlignmentBytes; - if (limits.uniformTexelBufferOffsetSingleTexelAlignment != VK_FALSE) { - uniform = (std::min)(uniform, texel); - } - return (std::max)((std::max)(storage, uniform), VkDeviceSize{1}); - } - /// Returns the current driver version provided in Vulkan-formatted version numbers. u32 GetDriverVersion() const { return properties.properties.driverVersion; @@ -370,6 +356,11 @@ public: return {count[0], count[1], count[2]}; } + /// Returns the maximum size for push constants. + VkDeviceSize GetMaxPushConstantsSize() const { + return properties.properties.limits.maxPushConstantsSize; + } + #define FN_MAX_LIMIT_LIST \ FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \ FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \ @@ -435,10 +426,6 @@ FN_MAX_LIMIT_LIST /// Returns true if the device supports float16 natively. bool IsFloat16Supported() const { - return features.shader_float16_int8.shaderFloat16 && !has_broken_float16_math; - } - - bool IsNativeFloat16Enabled() const { return features.shader_float16_int8.shaderFloat16; } @@ -571,6 +558,11 @@ FN_MAX_LIMIT_LIST return extensions.geometry_shader_passthrough; } + /// Returns true if the device supports VK_KHR_uniform_buffer_standard_layout. + bool IsKhrUniformBufferStandardLayoutSupported() const { + return extensions.uniform_buffer_standard_layout; + } + /// Returns true if the device supports VK_KHR_push_descriptor. bool IsKhrPushDescriptorSupported() const { return extensions.push_descriptor; @@ -643,9 +635,15 @@ FN_MAX_LIMIT_LIST return extensions.shader_stencil_export; } + /// Returns true if the device supports VK_KHR_create_renderpass2. + bool IsKhrCreateRenderPass2Supported() const { + return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2; + } + /// Returns true if the device supports VK_KHR_depth_stencil_resolve. bool IsKhrDepthStencilResolveSupported() const { - return extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2; + return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) && + IsKhrCreateRenderPass2Supported(); } /// Returns the supported resolve modes for the depth aspect. @@ -663,6 +661,13 @@ FN_MAX_LIMIT_LIST return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE; } + /// Returns true if depth/stencil operations can be performed efficiently. + /// Either through shader export or hardware blits. + bool CanPerformDepthStencilOperations() const { + return extensions.shader_stencil_export || is_blit_depth24_stencil8_supported || + is_blit_depth32_stencil8_supported; + } + /// Returns true if the device supports VK_EXT_depth_range_unrestricted. bool IsExtDepthRangeUnrestrictedSupported() const { return extensions.depth_range_unrestricted; @@ -673,14 +678,6 @@ FN_MAX_LIMIT_LIST return extensions.depth_clip_control; } - bool IsLoadOpNoneSupported() const { - return extensions.load_store_op_none || extensions.load_store_op_none_ext; - } - - bool IsExtDynamicRenderingUnusedAttachmentsSupported() const { - return extensions.dynamic_rendering_unused_attachments; - } - /// Returns true if the device supports VK_EXT_depth_bias_control. bool IsExtDepthBiasControlSupported() const { return extensions.depth_bias_control; @@ -711,6 +708,12 @@ FN_MAX_LIMIT_LIST return extensions.transform_feedback && properties.transform_feedback.transformFeedbackDraw; } + /// Returns true if transform feedback query types are supported. + bool IsTransformFeedbackQueriesSupported() const { + return extensions.transform_feedback && + properties.transform_feedback.transformFeedbackQueries; + } + /// Returns true if the device supports VK_EXT_transform_feedback properly. bool AreTransformFeedbackGeometryStreamsSupported() const { return features.transform_feedback.geometryStreams; @@ -745,6 +748,11 @@ FN_MAX_LIMIT_LIST !features.border_color_swizzle.borderColorSwizzleFromImage; } + /// Returns true if borderColorSwizzleFromImage is available. + bool IsBorderColorSwizzleFromImageSupported() const { + return features.border_color_swizzle.borderColorSwizzleFromImage; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; @@ -759,6 +767,11 @@ FN_MAX_LIMIT_LIST return features.extended_dynamic_state2.extendedDynamicState2LogicOp; } + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + bool IsExtExtendedDynamicState3Supported() const { + return extensions.extended_dynamic_state3; + } + /// Returns true if the device supports VK_EXT_4444_formats. bool IsExt4444FormatsSupported() const { return features.format_a4b4g4r4.formatA4B4G4R4; @@ -883,34 +896,6 @@ FN_MAX_LIMIT_LIST return extensions.conditional_rendering; } - VkPipelineStageFlags2 GetBufferUserStages() const { - VkPipelineStageFlags2 stages = vk::PIPELINE_STAGE_BUFFER_USERS; - if (IsExtConditionalRendering()) { - stages |= VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT; - } - if (IsExtTransformFeedbackSupported()) { - stages |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT; - } - return stages; - } - - VkPipelineStageFlags2 GetBufferConsumerStages() const { - return GetBufferUserStages() | VK_PIPELINE_STAGE_2_HOST_BIT; - } - - VkAccessFlags2 GetBufferConsumerAccess() const { - VkAccessFlags2 access = vk::ACCESS_BUFFER_CONSUMERS; - if (IsExtConditionalRendering()) { - access |= VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT; - } - if (IsExtTransformFeedbackSupported()) { - access |= VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | - VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT | - VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; - } - return access; - } - bool IsExtAstcDecodeModeSupported() const { return extensions.astc_decode_mode; } @@ -922,6 +907,11 @@ FN_MAX_LIMIT_LIST bool HasTimelineSemaphore() const; + /// Returns true if the device supports VK_KHR_synchronization2. + bool HasSynchronization2() const { + return extensions.synchronization2; + } + /// Returns the minimum supported version of SPIR-V. u32 SupportedSpirvVersion() const { if (instance_version >= VK_API_VERSION_1_3) { @@ -985,20 +975,16 @@ FN_MAX_LIMIT_LIST return must_emulate_scaled_formats; } - bool IsUMA() const { - return is_uma; - } - - bool IsHostImageCopySupported() const { - return extensions.host_image_copy; - } - bool HasNullDescriptor() const { return features.robustness2.nullDescriptor; } bool MustEmulateBGR565() const; + bool HasExactDepthBiasControl() const { + return features.depth_bias_control.depthBiasExact; + } + u32 GetMaxVertexInputAttributes() const { return properties.properties.limits.maxVertexInputAttributes; } @@ -1007,10 +993,6 @@ FN_MAX_LIMIT_LIST return properties.properties.limits.maxVertexInputBindings; } - u32 GetMaxVertexAttribDivisor() const { - return max_vertex_attrib_divisor; - } - u32 GetMaxViewports() const { return properties.properties.limits.maxViewports; } @@ -1027,35 +1009,63 @@ FN_MAX_LIMIT_LIST return features2.features.multiViewport; } + /// Returns true if the device supports VK_KHR_maintenance1. + bool IsKhrMaintenance1Supported() const { + return extensions.maintenance1; + } + + /// Returns true if the device supports VK_KHR_maintenance2. + bool IsKhrMaintenance2Supported() const { + return extensions.maintenance2; + } + + /// Returns true if the device supports VK_KHR_maintenance3. + bool IsKhrMaintenance3Supported() const { + return extensions.maintenance3; + } + /// Returns true if the device supports VK_KHR_maintenance4. bool IsKhrMaintenance4Supported() const { return extensions.maintenance4; } - /// Returns true if the device can read the draw count from a buffer. - bool IsDrawIndirectCountSupported() const { - return extensions.draw_indirect_count; - } - - /// Returns true if the device supports VK_EXT_vertex_attribute_divisor. - bool IsExtVertexAttributeDivisorSupported() const { - return extensions.vertex_attribute_divisor; - } - - /// Returns true if the device supports VK_KHR_sampler_mirror_clamp_to_edge. - bool IsKhrSamplerMirrorClampToEdgeSupported() const { - return extensions.sampler_mirror_clamp_to_edge; - } - + /// Returns true if the device supports VK_KHR_maintenance5. bool IsKhrMaintenance5Supported() const { return extensions.maintenance5; } + /// Returns true if polygon mode POINT supports gl_PointSize. + bool SupportsPolygonModePointSize() const { + return extensions.maintenance5 && properties.maintenance5.polygonModePointSize; + } + /// Returns true if depth/stencil swizzle ONE is supported. bool SupportsDepthStencilSwizzleOne() const { return extensions.maintenance5 && properties.maintenance5.depthStencilSwizzleOneSupport; } + /// Returns true if early fragment tests optimizations are available. + bool SupportsEarlyFragmentTests() const { + return extensions.maintenance5 && + properties.maintenance5.earlyFragmentMultisampleCoverageAfterSampleCounting && + properties.maintenance5.earlyFragmentSampleMaskTestBeforeSampleCounting; + } + + /// Returns true if the device supports VK_KHR_maintenance6. + bool IsKhrMaintenance6Supported() const { + return extensions.maintenance6; + } + + /// Returns true if the device supports VK_KHR_maintenance7. + bool IsKhrMaintenance7Supported() const { + return extensions.maintenance7; + } + + /// Returns true if the device supports VK_KHR_maintenance8. + bool IsKhrMaintenance8Supported() const { + return extensions.maintenance8; + } + /// Returns true if the device supports UINT8 index buffer conversion via compute shader. bool SupportsUint8Indices() const { return features.bit8_storage.storageBuffer8BitAccess && @@ -1130,6 +1140,9 @@ private: /// Returns true if the device natively supports blitting depth stencil images. bool TestDepthStencilBlits(VkFormat format) const; + void LoadStaticPipelineCache(); + void SaveStaticPipelineCache() const; + private: VkInstance instance; ///< Vulkan instance. VmaAllocator allocator; ///< VMA allocator. @@ -1138,6 +1151,8 @@ private: vk::Device logical; ///< Logical device. vk::Queue graphics_queue; ///< Main graphics queue. vk::Queue present_queue; ///< Main present queue. + vk::PipelineCache static_pipeline_cache; + bool owns_static_pipeline_cache{}; u32 instance_version{}; ///< Vulkan instance version. u32 graphics_family{}; ///< Main graphics queue family index. u32 present_family{}; ///< Main present queue family index. @@ -1183,12 +1198,10 @@ private: VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; - VkPhysicalDeviceTexelBufferAlignmentProperties texel_buffer_alignment{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{}; - VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{}; VkPhysicalDeviceProperties properties{}; }; @@ -1196,7 +1209,6 @@ private: Extensions extensions{}; Features features{}; Properties properties{}; - u32 max_vertex_attrib_divisor{1}; VkPhysicalDeviceFeatures2 features2{}; VkPhysicalDeviceProperties2 properties2{}; @@ -1207,10 +1219,10 @@ private: bool is_blit_depth32_stencil8_supported{}; ///< Support for blitting from and to D32S8. bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. bool is_integrated{}; ///< Is GPU an iGPU. - bool is_uma{}; + bool is_virtual{}; ///< Is GPU a virtual GPU. + bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. bool has_broken_compute{}; ///< Compute shaders can cause crashes bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit - bool has_broken_float16_math{}; bool has_broken_descriptor_aliasing{}; ///< Miscompiles descriptors aliased on one binding bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling. bool has_renderdoc{}; ///< Has RenderDoc attached diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index ef1143aeb6..f4721543ae 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -4,8 +4,20 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include +#include + +#include "common/alignment.h" +#include "common/assert.h" #include "common/common_types.h" #include "common/literals.h" +#include "common/logging.h" +#include #include "video_core/vulkan_common/vma.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" @@ -18,6 +30,26 @@ namespace { // Helpers translating MemoryUsage to flags/usage + [[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) { + switch (usage) { + case MemoryUsage::DeviceLocal: + return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + case MemoryUsage::Upload: + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + case MemoryUsage::Download: + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + case MemoryUsage::Stream: + return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + } + ASSERT_MSG(false, "Invalid memory usage={}", usage); + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + } + [[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { if (usage == MemoryUsage::Download) { return VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; @@ -26,13 +58,6 @@ namespace { : VkMemoryPropertyFlagBits{}; } - [[nodiscard]] VkMemoryPropertyFlags MemoryUsageRequiredVmaFlags(MemoryUsage usage) { - if (usage == MemoryUsage::Upload || usage == MemoryUsage::Download) { - return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - } - return VkMemoryPropertyFlagBits{}; - } - [[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { switch (usage) { case MemoryUsage::Upload: @@ -48,26 +73,6 @@ namespace { return {}; } - template - [[nodiscard]] VkResult AllocateRelaxing(VmaAllocationCreateInfo& ci, F&& allocate) { - VkResult result = allocate(ci); - if (result == VK_SUCCESS) { - return result; - } - if ((ci.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0) { - ci.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; - result = allocate(ci); - if (result == VK_SUCCESS) { - return result; - } - } - if ((ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { - ci.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - result = allocate(ci); - } - return result; - } - [[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) { switch (usage) { case MemoryUsage::DeviceLocal: @@ -81,9 +86,106 @@ namespace { } } // namespace +//MemoryCommit is now VMA-backed +MemoryCommit::MemoryCommit(VmaAllocator alloc, VmaAllocation a, + const VmaAllocationInfo &info) noexcept + : allocator{alloc}, allocation{a}, memory{info.deviceMemory}, + offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { + // Log GPU memory allocation + if (GPU::Logging::IsActive() && + Settings::values.gpu_log_memory_tracking.GetValue()) { + GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( + reinterpret_cast(memory), + static_cast(size), + 0 // Memory property flags (not easily available from VMA) + ); + } +} + +MemoryCommit::~MemoryCommit() { Release(); } + +MemoryCommit::MemoryCommit(MemoryCommit &&rhs) noexcept + : allocator{std::exchange(rhs.allocator, nullptr)}, + allocation{std::exchange(rhs.allocation, nullptr)}, + memory{std::exchange(rhs.memory, VK_NULL_HANDLE)}, + offset{std::exchange(rhs.offset, 0)}, + size{std::exchange(rhs.size, 0)}, + mapped_ptr{std::exchange(rhs.mapped_ptr, nullptr)} {} + +MemoryCommit &MemoryCommit::operator=(MemoryCommit &&rhs) noexcept { + if (this != &rhs) { + Release(); + allocator = std::exchange(rhs.allocator, nullptr); + allocation = std::exchange(rhs.allocation, nullptr); + memory = std::exchange(rhs.memory, VK_NULL_HANDLE); + offset = std::exchange(rhs.offset, 0); + size = std::exchange(rhs.size, 0); + mapped_ptr = std::exchange(rhs.mapped_ptr, nullptr); + } + return *this; +} + +std::span MemoryCommit::Map() +{ + if (!allocation) return {}; + if (!mapped_ptr) { + if (vmaMapMemory(allocator, allocation, &mapped_ptr) != VK_SUCCESS) return {}; + } + const size_t n = static_cast(std::min(size, + (std::numeric_limits::max)())); + return std::span{static_cast(mapped_ptr), n}; +} + +std::span MemoryCommit::Map() const +{ + if (!allocation) return {}; + if (!mapped_ptr) { + void *p = nullptr; + if (vmaMapMemory(allocator, allocation, &p) != VK_SUCCESS) return {}; + const_cast(this)->mapped_ptr = p; + } + const size_t n = static_cast(std::min(size, + (std::numeric_limits::max)())); + return std::span{static_cast(mapped_ptr), n}; +} + +void MemoryCommit::Unmap() +{ + if (allocation && mapped_ptr) { + vmaUnmapMemory(allocator, allocation); + mapped_ptr = nullptr; + } +} + +void MemoryCommit::Release() { + if (allocation && allocator) { + // Log GPU memory deallocation + if (GPU::Logging::IsActive() && + Settings::values.gpu_log_memory_tracking.GetValue() && + memory != VK_NULL_HANDLE) { + GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( + reinterpret_cast(memory) + ); + } + + if (mapped_ptr) { + vmaUnmapMemory(allocator, allocation); + mapped_ptr = nullptr; + } + vmaFreeMemory(allocator, allocation); + } + allocation = nullptr; + allocator = nullptr; + memory = VK_NULL_HANDLE; + offset = 0; + size = 0; +} + MemoryAllocator::MemoryAllocator(const Device &device_) : device{device_}, allocator{device.GetAllocator()}, - properties{device_.GetPhysical().GetMemoryProperties().memoryProperties} { + properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, + buffer_image_granularity{ + device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { // Preserve the previous "RenderDoc small heap" trimming behavior that we had in original vma minus the heap bug if (device.HasDebuggingToolAttached()) @@ -105,12 +207,12 @@ MemoryAllocator::~MemoryAllocator() = default; vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const { - VmaAllocationCreateInfo alloc_ci = { + const VmaAllocationCreateInfo alloc_ci = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, .requiredFlags = 0, .preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - .memoryTypeBits = valid_memory_types, + .memoryTypeBits = 0, .pool = VK_NULL_HANDLE, .pUserData = nullptr, .priority = 0.f, @@ -119,9 +221,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const VkImage handle{}; VmaAllocation allocation{}; VmaAllocationInfo alloc_info{}; - vk::Check(AllocateRelaxing(alloc_ci, [&](const VmaAllocationCreateInfo& retry) { - return vmaCreateImage(allocator, &ci, &retry, &handle, &allocation, &alloc_info); - })); + vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); // Log GPU memory allocation for images if (GPU::Logging::IsActive() && @@ -142,10 +242,10 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa auto const anv_flags = (usage == MemoryUsage::Stream && device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) ? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0; - VmaAllocationCreateInfo alloc_ci = { + const VmaAllocationCreateInfo alloc_ci = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), - .requiredFlags = MemoryUsageRequiredVmaFlags(usage), + .requiredFlags = 0, .preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, .pool = VK_NULL_HANDLE, @@ -158,9 +258,7 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa VmaAllocation allocation{}; VkMemoryPropertyFlags property_flags{}; - vk::Check(AllocateRelaxing(alloc_ci, [&](const VmaAllocationCreateInfo& retry) { - return vmaCreateBuffer(allocator, &ci, &retry, &handle, &allocation, &alloc_info); - })); + vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); // Log GPU memory allocation for buffers @@ -200,10 +298,10 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa if (usage == MemoryUsage::Stream) { memory_type_bits = 0u; } - VmaAllocationCreateInfo alloc_ci = { + const VmaAllocationCreateInfo alloc_ci = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), - .requiredFlags = MemoryUsageRequiredVmaFlags(usage), + .requiredFlags = 0, .preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, .memoryTypeBits = memory_type_bits, .pool = VK_NULL_HANDLE, @@ -216,10 +314,8 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa VmaAllocation allocation{}; VkMemoryPropertyFlags property_flags{}; - vk::Check(AllocateRelaxing(alloc_ci, [&](const VmaAllocationCreateInfo& retry) { - return vmaCreateBufferWithAlignment(allocator, &ci, &retry, min_alignment, &handle, - &allocation, &alloc_info); - })); + vk::Check(vmaCreateBufferWithAlignment(allocator, &ci, &alloc_ci, min_alignment, &handle, + &allocation, &alloc_info)); vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); u8 *data = reinterpret_cast(alloc_info.pMappedData); @@ -238,4 +334,75 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa location, device.GetDispatchLoader()); } +MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage) +{ + const auto vma_usage = MemoryUsageVma(usage); + VmaAllocationCreateInfo ci{}; + ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); + ci.usage = vma_usage; + ci.memoryTypeBits = reqs.memoryTypeBits & valid_memory_types; + ci.requiredFlags = 0; + ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); + + VmaAllocation a{}; + VmaAllocationInfo info{}; + + VkResult res = vmaAllocateMemory(allocator, &reqs, &ci, &a, &info); + + if (res != VK_SUCCESS) { + // Relax 1: drop budget constraint + auto ci2 = ci; + ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; + res = vmaAllocateMemory(allocator, &reqs, &ci2, &a, &info); + + // Relax 2: if we preferred DEVICE_LOCAL, drop that preference + if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + auto ci3 = ci2; + ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + res = vmaAllocateMemory(allocator, &reqs, &ci3, &a, &info); + } + } + + vk::Check(res); + return MemoryCommit(allocator, a, info); +} + +MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) { + // Allocate memory appropriate for this buffer automatically + const auto vma_usage = MemoryUsageVma(usage); + + VmaAllocationCreateInfo ci{}; + ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); + ci.usage = vma_usage; + ci.requiredFlags = 0; + ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); + ci.pool = VK_NULL_HANDLE; + ci.pUserData = nullptr; + ci.priority = 0.0f; + + const VkBuffer raw = *buffer; + + VmaAllocation a{}; + VmaAllocationInfo info{}; + + // Let VMA infer memory requirements from the buffer + VkResult res = vmaAllocateMemoryForBuffer(allocator, raw, &ci, &a, &info); + + if (res != VK_SUCCESS) { + auto ci2 = ci; + ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; + res = vmaAllocateMemoryForBuffer(allocator, raw, &ci2, &a, &info); + + if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + auto ci3 = ci2; + ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + res = vmaAllocateMemoryForBuffer(allocator, raw, &ci3, &a, &info); + } + } + + vk::Check(res); + vk::Check(vmaBindBufferMemory2(allocator, a, 0, raw, nullptr)); + return MemoryCommit(allocator, a, info); +} + } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index 79a33f0b7b..a0f9081fc0 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -39,6 +39,51 @@ namespace Vulkan { } } +/// Ownership handle of a memory commitment (real VMA allocation). + class MemoryCommit { + public: + MemoryCommit() noexcept = default; + + MemoryCommit(VmaAllocator allocator, VmaAllocation allocation, + const VmaAllocationInfo &info) noexcept; + + ~MemoryCommit(); + + MemoryCommit(const MemoryCommit &) = delete; + + MemoryCommit &operator=(const MemoryCommit &) = delete; + + MemoryCommit(MemoryCommit &&) noexcept; + + MemoryCommit &operator=(MemoryCommit &&) noexcept; + + [[nodiscard]] std::span Map(); + + [[nodiscard]] std::span Map() const; + + void Unmap(); + + explicit operator bool() const noexcept { return allocation != nullptr; } + + VkDeviceMemory Memory() const noexcept { return memory; } + + VkDeviceSize Offset() const noexcept { return offset; } + + VkDeviceSize Size() const noexcept { return size; } + + VmaAllocation Allocation() const noexcept { return allocation; } + + private: + void Release(); + + VmaAllocator allocator{}; ///< VMA allocator + VmaAllocation allocation{}; ///< VMA allocation handle + VkDeviceMemory memory{}; ///< Underlying VkDeviceMemory chosen by VMA + VkDeviceSize offset{}; ///< Offset of this allocation inside VkDeviceMemory + VkDeviceSize size{}; ///< Size of the allocation + void *mapped_ptr{}; ///< Optional persistent mapped pointer + }; + /// Memory allocator container. /// Allocates and releases memory allocations on demand. class MemoryAllocator { @@ -65,10 +110,35 @@ namespace Vulkan { vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage, VkDeviceSize min_alignment) const; + /** + * Commits a memory with the specified requirements. + * + * @param requirements Requirements returned from a Vulkan call. + * @param usage Indicates how the memory will be used. + * + * @returns A memory commit. + */ + MemoryCommit Commit(const VkMemoryRequirements &requirements, MemoryUsage usage); + + /// Commits memory required by the buffer and binds it (for buffers created outside VMA). + MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage); + private: + static bool IsAutoUsage(VmaMemoryUsage u) noexcept { + switch (u) { + case VMA_MEMORY_USAGE_AUTO: + case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE: + case VMA_MEMORY_USAGE_AUTO_PREFER_HOST: + return true; + default: + return false; + } + } + const Device &device; ///< Device handle. VmaAllocator allocator; ///< VMA allocator. const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties. + VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity u32 valid_memory_types{~0u}; }; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index c6b3afa2c7..bb43fe399f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -86,26 +86,26 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkAllocateCommandBuffers); X(vkAllocateDescriptorSets); X(vkAllocateMemory); - X(vkBindBufferMemory); - X(vkFreeMemory); X(vkBeginCommandBuffer); + X(vkBindBufferMemory); + X(vkBindImageMemory); X(vkCmdBeginConditionalRenderingEXT); X(vkCmdBeginQuery); - X(vkCmdBeginRendering); + X(vkCmdBeginRenderPass); X(vkCmdBeginTransformFeedbackEXT); + X(vkCmdBeginDebugUtilsLabelEXT); X(vkCmdBindDescriptorSets); X(vkCmdBindIndexBuffer); - X(vkCmdBindIndexBuffer2KHR); X(vkCmdBindPipeline); X(vkCmdBindTransformFeedbackBuffersEXT); X(vkCmdBindVertexBuffers); - X(vkCmdBlitImage2); + X(vkCmdBlitImage); X(vkCmdClearAttachments); X(vkCmdClearColorImage); - X(vkCmdCopyBuffer2); - X(vkCmdCopyBufferToImage2); - X(vkCmdCopyImage2); - X(vkCmdCopyImageToBuffer2); + X(vkCmdCopyBuffer); + X(vkCmdCopyBufferToImage); + X(vkCmdCopyImage); + X(vkCmdCopyImageToBuffer); X(vkCmdCopyQueryPoolResults); X(vkCmdDispatch); X(vkCmdDispatchIndirect); @@ -118,9 +118,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdDrawIndirectByteCountEXT); X(vkCmdEndConditionalRenderingEXT); X(vkCmdEndQuery); - X(vkCmdEndRendering); + X(vkCmdEndRenderPass); X(vkCmdEndTransformFeedbackEXT); + X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdFillBuffer); + X(vkCmdPipelineBarrier); X(vkCmdPipelineBarrier2); X(vkCmdPushConstants); X(vkCmdPushDescriptorSetWithTemplateKHR); @@ -128,11 +130,13 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetDepthBias); X(vkCmdSetDepthBias2EXT); X(vkCmdSetDepthBounds); + X(vkCmdSetEvent); X(vkCmdSetScissor); X(vkCmdSetStencilCompareMask); X(vkCmdSetStencilReference); X(vkCmdSetStencilWriteMask); X(vkCmdSetViewport); + X(vkCmdWaitEvents); X(vkCmdBindVertexBuffers2EXT); X(vkCmdSetCullModeEXT); X(vkCmdSetDepthBoundsTestEnableEXT); @@ -151,6 +155,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetDepthClampEnableEXT); X(vkCmdSetFrontFaceEXT); X(vkCmdSetLogicOpEXT); + X(vkCmdSetPatchControlPointsEXT); X(vkCmdSetLineWidth); X(vkCmdSetPrimitiveTopologyEXT); X(vkCmdSetStencilOpEXT); @@ -161,7 +166,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetColorBlendEnableEXT); X(vkCmdSetColorBlendEquationEXT); X(vkCmdResetQueryPool); - X(vkCmdResolveImage2); + X(vkCmdResolveImage); X(vkCreateBuffer); X(vkCreateBufferView); X(vkCreateCommandPool); @@ -169,12 +174,17 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreateDescriptorPool); X(vkCreateDescriptorSetLayout); X(vkCreateDescriptorUpdateTemplate); + X(vkCreateEvent); X(vkCreateFence); + X(vkCreateFramebuffer); X(vkCreateGraphicsPipelines); + X(vkCreateImage); X(vkCreateImageView); X(vkCreatePipelineCache); X(vkCreatePipelineLayout); X(vkCreateQueryPool); + X(vkCreateRenderPass); + X(vkCreateRenderPass2); X(vkCreateSampler); X(vkCreateSemaphore); X(vkCreateShaderModule); @@ -185,13 +195,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkDestroyDescriptorPool); X(vkDestroyDescriptorSetLayout); X(vkDestroyDescriptorUpdateTemplate); + X(vkDestroyEvent); X(vkDestroyFence); + X(vkDestroyFramebuffer); X(vkDestroyImage); X(vkDestroyImageView); X(vkDestroyPipeline); X(vkDestroyPipelineCache); X(vkDestroyPipelineLayout); X(vkDestroyQueryPool); + X(vkDestroyRenderPass); X(vkDestroySampler); X(vkDestroySemaphore); X(vkDestroyShaderModule); @@ -200,22 +213,30 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkEndCommandBuffer); X(vkFreeCommandBuffers); X(vkFreeDescriptorSets); + X(vkFreeMemory); X(vkGetBufferMemoryRequirements2); - X(vkGetDeviceBufferMemoryRequirements); X(vkGetDeviceQueue); + X(vkGetEventStatus); X(vkGetFenceStatus); + X(vkGetImageMemoryRequirements); X(vkGetPipelineCacheData); + X(vkGetMemoryFdKHR); +#ifdef _WIN32 + X(vkGetMemoryWin32HandleKHR); +#endif X(vkGetQueryPoolResults); X(vkGetPipelineExecutablePropertiesKHR); X(vkGetPipelineExecutableStatisticsKHR); X(vkGetSemaphoreCounterValue); - X(vkCopyMemoryToImageEXT); - X(vkTransitionImageLayoutEXT); + X(vkMapMemory); X(vkQueueBindSparse); + X(vkQueueSubmit); X(vkQueueSubmit2); X(vkResetFences); X(vkResetQueryPool); X(vkSetDebugUtilsObjectNameEXT); + X(vkSetDebugUtilsObjectTagEXT); + X(vkUnmapMemory); X(vkUpdateDescriptorSetWithTemplate); X(vkUpdateDescriptorSets); X(vkGetBufferDeviceAddress); @@ -244,12 +265,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device); } - // Maintenance4 queries are core in Vulkan 1.3, otherwise require VK_KHR_maintenance4 - if (!dld.vkGetDeviceBufferMemoryRequirements) { - Proc(dld.vkGetDeviceBufferMemoryRequirements, dld, - "vkGetDeviceBufferMemoryRequirementsKHR", device); - } - // Synchronization2 is core in Vulkan 1.3, otherwise requires VK_KHR_synchronization2 if (!dld.vkCmdPipelineBarrier2) { Proc(dld.vkCmdPipelineBarrier2, dld, "vkCmdPipelineBarrier2KHR", device); @@ -258,19 +273,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device); } - const auto load_khr = [&](auto& function, const char* name) { - if (!function) { - Proc(function, dld, name, device); - } - }; - load_khr(dld.vkCmdBeginRendering, "vkCmdBeginRenderingKHR"); - load_khr(dld.vkCmdEndRendering, "vkCmdEndRenderingKHR"); - load_khr(dld.vkCmdBlitImage2, "vkCmdBlitImage2KHR"); - load_khr(dld.vkCmdCopyBuffer2, "vkCmdCopyBuffer2KHR"); - load_khr(dld.vkCmdCopyBufferToImage2, "vkCmdCopyBufferToImage2KHR"); - load_khr(dld.vkCmdCopyImage2, "vkCmdCopyImage2KHR"); - load_khr(dld.vkCmdCopyImageToBuffer2, "vkCmdCopyImageToBuffer2KHR"); - load_khr(dld.vkCmdResolveImage2, "vkCmdResolveImage2KHR"); + if (!dld.vkCreateRenderPass2) { + Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device); + } #undef X } @@ -303,7 +308,9 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { // These functions may fail to load depending on the enabled extensions. // Don't return a failure on these. X(vkCreateDebugUtilsMessengerEXT); + X(vkCreateDebugReportCallbackEXT); X(vkDestroyDebugUtilsMessengerEXT); + X(vkDestroyDebugReportCallbackEXT); X(vkDestroySurfaceKHR); X(vkGetPhysicalDeviceFeatures2); X(vkGetPhysicalDeviceFormatProperties2); @@ -316,10 +323,10 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { X(vkGetSwapchainImagesKHR); X(vkQueuePresentKHR); - return X(vkCreateDevice) && X(vkDestroyDevice) && + return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && - X(vkGetPhysicalDeviceMemoryProperties2) && + X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) && X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties); #undef X } @@ -340,10 +347,6 @@ void Destroy(VkDevice device, VkBuffer handle, const DeviceDispatch& dld) noexce dld.vkDestroyBuffer(device, handle, nullptr); } -void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { - dld.vkFreeMemory(device, handle, nullptr); -} - void Destroy(VkDevice device, VkBufferView handle, const DeviceDispatch& dld) noexcept { dld.vkDestroyBufferView(device, handle, nullptr); } @@ -365,10 +368,22 @@ void Destroy(VkDevice device, VkDescriptorUpdateTemplate handle, dld.vkDestroyDescriptorUpdateTemplate(device, handle, nullptr); } +void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { + dld.vkFreeMemory(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkEvent handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyEvent(device, handle, nullptr); +} + void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { dld.vkDestroyFence(device, handle, nullptr); } +void Destroy(VkDevice device, VkFramebuffer handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyFramebuffer(device, handle, nullptr); +} + void Destroy(VkDevice device, VkImage handle, const DeviceDispatch& dld) noexcept { dld.vkDestroyImage(device, handle, nullptr); } @@ -393,6 +408,10 @@ void Destroy(VkDevice device, VkQueryPool handle, const DeviceDispatch& dld) noe dld.vkDestroyQueryPool(device, handle, nullptr); } +void Destroy(VkDevice device, VkRenderPass handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyRenderPass(device, handle, nullptr); +} + void Destroy(VkDevice device, VkSampler handle, const DeviceDispatch& dld) noexcept { dld.vkDestroySampler(device, handle, nullptr); } @@ -414,6 +433,11 @@ void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle, dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr); } +void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle, + const InstanceDispatch& dld) noexcept { + dld.vkDestroyDebugReportCallbackEXT(instance, handle, nullptr); +} + void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { dld.vkDestroySurfaceKHR(instance, handle, nullptr); } @@ -437,7 +461,7 @@ Instance Instance::Create(u32 version, Span layers, SpanvkCreateDebugReportCallbackEXT(handle, &create_info, nullptr, &object)); + return DebugReportCallback(object, handle, *dld); +} + void Image::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); } @@ -523,10 +554,44 @@ void ImageView::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE_VIEW, name); } +int DeviceMemory::GetMemoryFdKHR() const { + const VkMemoryGetFdInfoKHR get_fd_info{ + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .pNext = nullptr, + .memory = handle, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, + }; + int fd; + Check(dld->vkGetMemoryFdKHR(owner, &get_fd_info, &fd)); + return fd; +} + +#ifdef _WIN32 +HANDLE DeviceMemory::GetMemoryWin32HandleKHR() const { + const VkMemoryGetWin32HandleInfoKHR get_win32_handle_info{ + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, + .pNext = nullptr, + .memory = handle, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, + }; + HANDLE win32_handle; + Check(dld->vkGetMemoryWin32HandleKHR(owner, &get_win32_handle_info, &win32_handle)); + return win32_handle; +} +#endif + +void DeviceMemory::SetObjectNameEXT(const char* name) const { + SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_DEVICE_MEMORY, name); +} + void Fence::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_FENCE, name); } +void Framebuffer::SetObjectNameEXT(const char* name) const { + SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_FRAMEBUFFER, name); +} + DescriptorSets DescriptorPool::Allocate(const VkDescriptorSetAllocateInfo& ai) const { const std::size_t num = ai.descriptorSetCount; std::unique_ptr sets = std::make_unique(num); @@ -578,6 +643,10 @@ std::vector SwapchainKHR::GetImages() const { return images; } +void Event::SetObjectNameEXT(const char* name) const { + SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_EVENT, name); +} + void ShaderModule::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); } @@ -623,37 +692,6 @@ BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { return BufferView(object, handle, *dld); } -ExternalBuffer Device::CreateExternalBuffer(const VkBufferCreateInfo& ci) const { - VkBuffer object; - Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object)); - return ExternalBuffer(object, handle, *dld); -} - -DeviceMemory Device::AllocateMemory(const VkMemoryAllocateInfo& ai) const { - VkDeviceMemory object; - Check(dld->vkAllocateMemory(handle, &ai, nullptr, &object)); - return DeviceMemory(object, handle, *dld); -} - -void Device::BindBufferMemory(VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize offset) const { - Check(dld->vkBindBufferMemory(handle, buffer, memory, offset)); -} - -VkMemoryRequirements Device::GetBufferMemoryRequirements(VkBuffer buffer) const noexcept { - const VkBufferMemoryRequirementsInfo2 info{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, - .pNext = nullptr, - .buffer = buffer, - }; - VkMemoryRequirements2 requirements{ - .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, - .pNext = nullptr, - .memoryRequirements = {}, - }; - dld->vkGetBufferMemoryRequirements2(handle, &info, &requirements); - return requirements.memoryRequirements; -} - ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { VkImageView object; Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); @@ -687,6 +725,18 @@ DescriptorPool Device::CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci return DescriptorPool(object, handle, *dld); } +RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { + VkRenderPass object; + Check(dld->vkCreateRenderPass(handle, &ci, nullptr, &object)); + return RenderPass(object, handle, *dld); +} + +RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const { + VkRenderPass object; + Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object)); + return RenderPass(object, handle, *dld); +} + DescriptorSetLayout Device::CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const { VkDescriptorSetLayout object; @@ -731,6 +781,12 @@ Sampler Device::CreateSampler(const VkSamplerCreateInfo& ci) const { return Sampler(object, handle, *dld); } +Framebuffer Device::CreateFramebuffer(const VkFramebufferCreateInfo& ci) const { + VkFramebuffer object; + Check(dld->vkCreateFramebuffer(handle, &ci, nullptr, &object)); + return Framebuffer(object, handle, *dld); +} + CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const { VkCommandPool object; Check(dld->vkCreateCommandPool(handle, &ci, nullptr, &object)); @@ -756,28 +812,60 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons return ShaderModule(object, handle, *dld); } +Event Device::CreateEvent() const { + static constexpr VkEventCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + }; + + VkEvent object; + Check(dld->vkCreateEvent(handle, &ci, nullptr, &object)); + return Event(object, handle, *dld); +} + SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { VkSwapchainKHR object; Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); return SwapchainKHR(object, handle, *dld); } -VkMemoryRequirements Device::GetDeviceBufferMemoryRequirements( - const VkBufferCreateInfo& ci) const noexcept { - const VkDeviceBufferMemoryRequirements info{ - .sType = VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS, +DeviceMemory Device::TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept { + VkDeviceMemory memory; + if (dld->vkAllocateMemory(handle, &ai, nullptr, &memory) != VK_SUCCESS) { + return {}; + } + return DeviceMemory(memory, handle, *dld); +} + +DeviceMemory Device::AllocateMemory(const VkMemoryAllocateInfo& ai) const { + VkDeviceMemory memory; + Check(dld->vkAllocateMemory(handle, &ai, nullptr, &memory)); + return DeviceMemory(memory, handle, *dld); +} + +VkMemoryRequirements Device::GetBufferMemoryRequirements(VkBuffer buffer, + void* pnext) const noexcept { + const VkBufferMemoryRequirementsInfo2 info{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, .pNext = nullptr, - .pCreateInfo = &ci, + .buffer = buffer, }; VkMemoryRequirements2 requirements{ .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, - .pNext = nullptr, + .pNext = pnext, .memoryRequirements{}, }; - dld->vkGetDeviceBufferMemoryRequirements(handle, &info, &requirements); + dld->vkGetBufferMemoryRequirements2(handle, &info, &requirements); return requirements.memoryRequirements; } +VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noexcept { + VkMemoryRequirements requirements; + dld->vkGetImageMemoryRequirements(handle, image, &requirements); + return requirements; +} + std::vector Device::GetPipelineExecutablePropertiesKHR( VkPipeline pipeline) const { const VkPipelineInfoKHR info{ @@ -829,6 +917,14 @@ void PhysicalDevice::GetProperties2(VkPhysicalDeviceProperties2& properties) con dld->vkGetPhysicalDeviceProperties2(physical_device, &properties); } +VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { + VkPhysicalDeviceFeatures2 features2; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2.pNext = nullptr; + dld->vkGetPhysicalDeviceFeatures2(physical_device, &features2); + return features2.features; +} + void PhysicalDevice::GetFeatures2(VkPhysicalDeviceFeatures2& features) const noexcept { dld->vkGetPhysicalDeviceFeatures2(physical_device, &features); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index bd9bd1b5bd..97d2ba3700 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -147,53 +147,16 @@ inline VkResult Filter(VkResult result) { return result; } -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_2_HOST_BIT; +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE = + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_GRAPHICS_SHADERS = - VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER = + PIPELINE_STAGE_GRAPHICS_COMPUTE | VK_PIPELINE_STAGE_TRANSFER_BIT; -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_BUFFER_INPUTS = - VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT | - PIPELINE_STAGE_GRAPHICS_SHADERS | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_HOST = + PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER | VK_PIPELINE_STAGE_HOST_BIT; -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_ATTACHMENTS = - VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; - -inline constexpr VkAccessFlags2 ACCESS_SHADER_RESOURCES = - VK_ACCESS_2_UNIFORM_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT; - -inline constexpr VkAccessFlags2 ACCESS_BUFFER_INPUTS = - VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_2_INDEX_READ_BIT | - VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | ACCESS_SHADER_RESOURCES; - -inline constexpr VkAccessFlags2 ACCESS_ATTACHMENTS = - VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - -inline constexpr VkAccessFlags2 ACCESS_TRANSFER = - VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT; - -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_BUFFER_USERS = - PIPELINE_STAGE_BUFFER_INPUTS | VK_PIPELINE_STAGE_2_TRANSFER_BIT; - -inline constexpr VkAccessFlags2 ACCESS_BUFFER_WRITES = - VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT; - -inline constexpr VkAccessFlags2 ACCESS_BUFFER_CONSUMERS = - ACCESS_BUFFER_INPUTS | ACCESS_TRANSFER | VK_ACCESS_2_HOST_READ_BIT; - -inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_IMAGE_USERS = - PIPELINE_STAGE_GRAPHICS_SHADERS | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT | - PIPELINE_STAGE_ATTACHMENTS | VK_PIPELINE_STAGE_2_TRANSFER_BIT; - -inline constexpr VkAccessFlags2 ACCESS_IMAGE_WRITES = - VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT; - -inline constexpr VkAccessFlags2 ACCESS_IMAGE_USERS = - VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT | ACCESS_ATTACHMENTS | - ACCESS_TRANSFER; +inline constexpr VkPipelineStageFlags PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_HOST_BIT; /// Table holding Vulkan instance function pointers. @@ -206,8 +169,10 @@ struct InstanceDispatch { PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{}; PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{}; + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT{}; PFN_vkCreateDevice vkCreateDevice{}; PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{}; + PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT{}; PFN_vkDestroyDevice vkDestroyDevice{}; PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{}; PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; @@ -216,6 +181,7 @@ struct InstanceDispatch { PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2{}; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2{}; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2{}; @@ -235,27 +201,27 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers{}; PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets{}; PFN_vkAllocateMemory vkAllocateMemory{}; - PFN_vkBindBufferMemory vkBindBufferMemory{}; - PFN_vkFreeMemory vkFreeMemory{}; PFN_vkBeginCommandBuffer vkBeginCommandBuffer{}; + PFN_vkBindBufferMemory vkBindBufferMemory{}; + PFN_vkBindImageMemory vkBindImageMemory{}; PFN_vkCmdBeginConditionalRenderingEXT vkCmdBeginConditionalRenderingEXT{}; + PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{}; PFN_vkCmdBeginQuery vkCmdBeginQuery{}; - PFN_vkCmdBeginRendering vkCmdBeginRendering{}; + PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{}; PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{}; PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{}; PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{}; - PFN_vkCmdBindIndexBuffer2KHR vkCmdBindIndexBuffer2KHR{}; PFN_vkCmdBindPipeline vkCmdBindPipeline{}; PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{}; PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{}; PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{}; - PFN_vkCmdBlitImage2 vkCmdBlitImage2{}; + PFN_vkCmdBlitImage vkCmdBlitImage{}; PFN_vkCmdClearAttachments vkCmdClearAttachments{}; PFN_vkCmdClearColorImage vkCmdClearColorImage{}; - PFN_vkCmdCopyBuffer2 vkCmdCopyBuffer2{}; - PFN_vkCmdCopyBufferToImage2 vkCmdCopyBufferToImage2{}; - PFN_vkCmdCopyImage2 vkCmdCopyImage2{}; - PFN_vkCmdCopyImageToBuffer2 vkCmdCopyImageToBuffer2{}; + PFN_vkCmdCopyBuffer vkCmdCopyBuffer{}; + PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage{}; + PFN_vkCmdCopyImage vkCmdCopyImage{}; + PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults{}; PFN_vkCmdDispatch vkCmdDispatch{}; PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect{}; @@ -267,15 +233,17 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{}; PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT{}; PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT{}; + PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; PFN_vkCmdEndQuery vkCmdEndQuery{}; - PFN_vkCmdEndRendering vkCmdEndRendering{}; + PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{}; + PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; PFN_vkCmdPipelineBarrier2 vkCmdPipelineBarrier2{}; PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; PFN_vkCmdResetQueryPool vkCmdResetQueryPool{}; - PFN_vkCmdResolveImage2 vkCmdResolveImage2{}; + PFN_vkCmdResolveImage vkCmdResolveImage{}; PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{}; PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{}; PFN_vkCmdSetDepthBias vkCmdSetDepthBias{}; @@ -292,10 +260,13 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT{}; PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{}; PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{}; + PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT{}; PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{}; PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{}; PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{}; + PFN_vkCmdSetEvent vkCmdSetEvent{}; PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; + PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{}; PFN_vkCmdSetLogicOpEXT vkCmdSetLogicOpEXT{}; PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; @@ -311,6 +282,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT{}; PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; + PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; PFN_vkCreateCommandPool vkCreateCommandPool{}; @@ -318,12 +290,17 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreateDescriptorPool vkCreateDescriptorPool{}; PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{}; PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate{}; + PFN_vkCreateEvent vkCreateEvent{}; PFN_vkCreateFence vkCreateFence{}; + PFN_vkCreateFramebuffer vkCreateFramebuffer{}; PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; + PFN_vkCreateImage vkCreateImage{}; PFN_vkCreateImageView vkCreateImageView{}; PFN_vkCreatePipelineCache vkCreatePipelineCache{}; PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; + PFN_vkCreateRenderPass vkCreateRenderPass{}; + PFN_vkCreateRenderPass2 vkCreateRenderPass2{}; PFN_vkCreateSampler vkCreateSampler{}; PFN_vkCreateSemaphore vkCreateSemaphore{}; PFN_vkCreateShaderModule vkCreateShaderModule{}; @@ -334,13 +311,16 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{}; PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{}; PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate{}; + PFN_vkDestroyEvent vkDestroyEvent{}; PFN_vkDestroyFence vkDestroyFence{}; + PFN_vkDestroyFramebuffer vkDestroyFramebuffer{}; PFN_vkDestroyImage vkDestroyImage{}; PFN_vkDestroyImageView vkDestroyImageView{}; PFN_vkDestroyPipeline vkDestroyPipeline{}; PFN_vkDestroyPipelineCache vkDestroyPipelineCache{}; PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; PFN_vkDestroyQueryPool vkDestroyQueryPool{}; + PFN_vkDestroyRenderPass vkDestroyRenderPass{}; PFN_vkDestroySampler vkDestroySampler{}; PFN_vkDestroySemaphore vkDestroySemaphore{}; PFN_vkDestroyShaderModule vkDestroyShaderModule{}; @@ -349,22 +329,30 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkEndCommandBuffer vkEndCommandBuffer{}; PFN_vkFreeCommandBuffers vkFreeCommandBuffers{}; PFN_vkFreeDescriptorSets vkFreeDescriptorSets{}; + PFN_vkFreeMemory vkFreeMemory{}; PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{}; - PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements{}; PFN_vkGetDeviceQueue vkGetDeviceQueue{}; + PFN_vkGetEventStatus vkGetEventStatus{}; PFN_vkGetFenceStatus vkGetFenceStatus{}; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; PFN_vkGetPipelineCacheData vkGetPipelineCacheData{}; + PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; +#ifdef _WIN32 + PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; +#endif PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{}; - PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImageEXT{}; - PFN_vkTransitionImageLayoutEXT vkTransitionImageLayoutEXT{}; + PFN_vkMapMemory vkMapMemory{}; PFN_vkQueueBindSparse vkQueueBindSparse{}; + PFN_vkQueueSubmit vkQueueSubmit{}; PFN_vkQueueSubmit2 vkQueueSubmit2{}; PFN_vkResetFences vkResetFences{}; PFN_vkResetQueryPool vkResetQueryPool{}; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; + PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{}; + PFN_vkUnmapMemory vkUnmapMemory{}; PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate{}; PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress{}; PFN_vkGetDescriptorSetLayoutSizeEXT vkGetDescriptorSetLayoutSizeEXT{}; @@ -390,23 +378,27 @@ void Destroy(VkDevice, const InstanceDispatch&) noexcept; void Destroy(VkDevice, VkBuffer, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept; -void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorUpdateTemplate, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkSampler, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept; void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept; +void Destroy(VkInstance, VkDebugReportCallbackEXT, const InstanceDispatch&) noexcept; void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept; VkResult Free(VkDevice, VkDescriptorPool, Span, const DeviceDispatch&) noexcept; @@ -644,14 +636,14 @@ private: }; using DebugUtilsMessenger = Handle; +using DebugReportCallback = Handle; using DescriptorSetLayout = Handle; using DescriptorUpdateTemplate = Handle; using Pipeline = Handle; using PipelineLayout = Handle; using QueryPool = Handle; +using RenderPass = Handle; using Sampler = Handle; -using DeviceMemory = Handle; -using ExternalBuffer = Handle; using SurfaceKHR = Handle; using DescriptorSets = PoolAllocations; @@ -677,6 +669,11 @@ public: [[nodiscard]] DebugUtilsMessenger CreateDebugUtilsMessenger( const VkDebugUtilsMessengerCreateInfoEXT& create_info) const; + /// Creates a debug report callback. + /// @throw Exception on creation failure. + [[nodiscard]] DebugReportCallback CreateDebugReportCallback( + const VkDebugReportCallbackCreateInfoEXT& create_info) const; + /// Returns dispatch table. const InstanceDispatch& Dispatch() const noexcept { return *dld; @@ -849,7 +846,13 @@ public: constexpr Queue(VkQueue queue_, const DeviceDispatch& dld_) noexcept : queue{queue_}, dld{&dld_} {} - VkResult Submit(Span submit_infos, + VkResult Submit(Span submit_infos, + VkFence fence = VK_NULL_HANDLE) const noexcept { + return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence); + } + + /// Submits using VK_KHR_synchronization2 / Vulkan 1.3 vkQueueSubmit2. + VkResult Submit2(Span submit_infos, VkFence fence = VK_NULL_HANDLE) const noexcept { return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence); } @@ -884,6 +887,30 @@ public: void SetObjectNameEXT(const char* name) const; }; +class DeviceMemory : public Handle { + using Handle::Handle; + +public: + int GetMemoryFdKHR() const; + +#ifdef _WIN32 + HANDLE GetMemoryWin32HandleKHR() const; +#endif + + /// Set object name. + void SetObjectNameEXT(const char* name) const; + + u8* Map(VkDeviceSize offset, VkDeviceSize size) const { + void* data; + Check(dld->vkMapMemory(owner, handle, offset, size, 0, &data)); + return static_cast(data); + } + + void Unmap() const noexcept { + dld->vkUnmapMemory(owner, handle); + } +}; + class Fence : public Handle { using Handle::Handle; @@ -904,6 +931,14 @@ public: } }; +class Framebuffer : public Handle { + using Handle::Handle; + +public: + /// Set object name. + void SetObjectNameEXT(const char* name) const; +}; + class DescriptorPool : public Handle { using Handle::Handle; @@ -932,6 +967,18 @@ public: std::vector GetImages() const; }; +class Event : public Handle { + using Handle::Handle; + +public: + /// Set object name. + void SetObjectNameEXT(const char* name) const; + + VkResult GetStatus() const noexcept { + return dld->vkGetEventStatus(owner, handle); + } +}; + class ShaderModule : public Handle { using Handle::Handle; @@ -1006,14 +1053,6 @@ public: [[nodiscard]] BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; - [[nodiscard]] ExternalBuffer CreateExternalBuffer(const VkBufferCreateInfo& ci) const; - - [[nodiscard]] DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; - - void BindBufferMemory(VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize offset) const; - - [[nodiscard]] VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer) const noexcept; - [[nodiscard]] ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; [[nodiscard]] Semaphore CreateSemaphore() const; @@ -1024,6 +1063,10 @@ public: [[nodiscard]] DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; + [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + + [[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const; + [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const; @@ -1039,6 +1082,8 @@ public: [[nodiscard]] Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; + [[nodiscard]] Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const; + [[nodiscard]] CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; [[nodiscard]] DescriptorUpdateTemplate CreateDescriptorUpdateTemplate( @@ -1048,9 +1093,18 @@ public: [[nodiscard]] ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; + [[nodiscard]] Event CreateEvent() const; + [[nodiscard]] SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; - VkMemoryRequirements GetDeviceBufferMemoryRequirements(const VkBufferCreateInfo& ci) const noexcept; + [[nodiscard]] DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; + + [[nodiscard]] DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; + + VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer, + void* pnext = nullptr) const noexcept; + + VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; std::vector GetPipelineExecutablePropertiesKHR( VkPipeline pipeline) const; @@ -1108,14 +1162,6 @@ public: dld->vkResetQueryPool(handle, query_pool, first, count); } - void CopyMemoryToImageEXT(const VkCopyMemoryToImageInfoEXT& info) const { - Check(dld->vkCopyMemoryToImageEXT(handle, &info)); - } - - void TransitionImageLayoutEXT(const VkHostImageLayoutTransitionInfoEXT& info) const { - Check(dld->vkTransitionImageLayoutEXT(handle, 1, &info)); - } - VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, void* data, VkDeviceSize stride, VkQueryResultFlags flags) const noexcept { @@ -1140,6 +1186,8 @@ public: void GetProperties2(VkPhysicalDeviceProperties2&) const noexcept; + VkPhysicalDeviceFeatures GetFeatures() const noexcept; + void GetFeatures2(VkPhysicalDeviceFeatures2&) const noexcept; VkFormatProperties GetFormatProperties(VkFormat) const noexcept; @@ -1190,12 +1238,13 @@ public: Check(dld->vkEndCommandBuffer(handle)); } - void BeginRendering(const VkRenderingInfo& rendering_info) const noexcept { - dld->vkCmdBeginRendering(handle, &rendering_info); + void BeginRenderPass(const VkRenderPassBeginInfo& renderpass_bi, + VkSubpassContents contents) const noexcept { + dld->vkCmdBeginRenderPass(handle, &renderpass_bi, contents); } - void EndRendering() const noexcept { - dld->vkCmdEndRendering(handle); + void EndRenderPass() const noexcept { + dld->vkCmdEndRenderPass(handle); } void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept { @@ -1231,11 +1280,6 @@ public: dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); } - void BindIndexBuffer2KHR(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, - VkIndexType index_type) const noexcept { - dld->vkCmdBindIndexBuffer2KHR(handle, buffer, offset, size, index_type); - } - void BindVertexBuffers(u32 first, u32 count, const VkBuffer* buffers, const VkDeviceSize* offsets) const noexcept { dld->vkCmdBindVertexBuffers(handle, first, count, buffers, offsets); @@ -1298,35 +1342,16 @@ public: } void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, - VkImageLayout dst_layout, Span regions, + VkImageLayout dst_layout, Span regions, VkFilter filter) const noexcept { - const VkBlitImageInfo2 info{ - .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, - .pNext = nullptr, - .srcImage = src_image, - .srcImageLayout = src_layout, - .dstImage = dst_image, - .dstImageLayout = dst_layout, - .regionCount = regions.size(), - .pRegions = regions.data(), - .filter = filter, - }; - dld->vkCmdBlitImage2(handle, &info); + dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), + regions.data(), filter); } void ResolveImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, - VkImageLayout dst_layout, Span regions) const noexcept { - const VkResolveImageInfo2 info{ - .sType = VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2, - .pNext = nullptr, - .srcImage = src_image, - .srcImageLayout = src_layout, - .dstImage = dst_image, - .dstImageLayout = dst_layout, - .regionCount = regions.size(), - .pRegions = regions.data(), - }; - dld->vkCmdResolveImage2(handle, &info); + VkImageLayout dst_layout, Span regions) { + dld->vkCmdResolveImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), + regions.data()); } void Dispatch(u32 x, u32 y, u32 z) const noexcept { @@ -1337,50 +1362,103 @@ public: dld->vkCmdDispatchIndirect(handle, indirect_buffer, offset); } - void PipelineBarrier(VkDependencyFlags dependency_flags, - Span memory_barriers, - Span buffer_barriers, - Span image_barriers) const noexcept { - const VkDependencyInfo dependency_info{ - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = dependency_flags, - .memoryBarrierCount = memory_barriers.size(), - .pMemoryBarriers = memory_barriers.data(), - .bufferMemoryBarrierCount = buffer_barriers.size(), - .pBufferMemoryBarriers = buffer_barriers.data(), - .imageMemoryBarrierCount = image_barriers.size(), - .pImageMemoryBarriers = image_barriers.data(), - }; - dld->vkCmdPipelineBarrier2(handle, &dependency_info); + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependency_flags, Span memory_barriers, + Span buffer_barriers, + Span image_barriers) const noexcept { + static constexpr u32 MaxBarriers = 16; + if (dld->vkCmdPipelineBarrier2 && memory_barriers.size() <= MaxBarriers && + buffer_barriers.size() <= MaxBarriers && image_barriers.size() <= MaxBarriers) { + const auto src_stage_mask2 = static_cast(src_stage_mask); + const auto dst_stage_mask2 = static_cast(dst_stage_mask); + + std::array memory_barriers2; + for (u32 i = 0; i < memory_barriers.size(); ++i) { + memory_barriers2[i] = VkMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(memory_barriers[i].srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(memory_barriers[i].dstAccessMask), + }; + } + std::array buffer_barriers2; + for (u32 i = 0; i < buffer_barriers.size(); ++i) { + const auto& barrier = buffer_barriers[i]; + buffer_barriers2[i] = VkBufferMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(barrier.srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(barrier.dstAccessMask), + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .buffer = barrier.buffer, + .offset = barrier.offset, + .size = barrier.size, + }; + } + std::array image_barriers2; + for (u32 i = 0; i < image_barriers.size(); ++i) { + const auto& barrier = image_barriers[i]; + image_barriers2[i] = VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = src_stage_mask2, + .srcAccessMask = static_cast(barrier.srcAccessMask), + .dstStageMask = dst_stage_mask2, + .dstAccessMask = static_cast(barrier.dstAccessMask), + .oldLayout = barrier.oldLayout, + .newLayout = barrier.newLayout, + .srcQueueFamilyIndex = barrier.srcQueueFamilyIndex, + .dstQueueFamilyIndex = barrier.dstQueueFamilyIndex, + .image = barrier.image, + .subresourceRange = barrier.subresourceRange, + }; + } + const VkDependencyInfo dependency_info{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = dependency_flags, + .memoryBarrierCount = memory_barriers.size(), + .pMemoryBarriers = memory_barriers2.data(), + .bufferMemoryBarrierCount = buffer_barriers.size(), + .pBufferMemoryBarriers = buffer_barriers2.data(), + .imageMemoryBarrierCount = image_barriers.size(), + .pImageMemoryBarriers = image_barriers2.data(), + }; + dld->vkCmdPipelineBarrier2(handle, &dependency_info); + return; + } + dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags, + memory_barriers.size(), memory_barriers.data(), + buffer_barriers.size(), buffer_barriers.data(), + image_barriers.size(), image_barriers.data()); } - void PipelineBarrier(VkPipelineStageFlags2 src_stage_mask, VkPipelineStageFlags2 dst_stage_mask, + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkDependencyFlags dependency_flags = 0) const noexcept { - const VkMemoryBarrier2 barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = src_stage_mask, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = dst_stage_mask, - .dstAccessMask = VK_ACCESS_2_NONE, - }; - PipelineBarrier(dependency_flags, barrier, {}, {}); + PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, {}, {}, {}); } - void PipelineBarrier(const VkMemoryBarrier2& memory_barrier, - VkDependencyFlags dependency_flags = 0) const noexcept { - PipelineBarrier(dependency_flags, memory_barrier, {}, {}); + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependency_flags, + const VkMemoryBarrier& memory_barrier) const noexcept { + PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, memory_barrier, {}, {}); } - void PipelineBarrier(const VkBufferMemoryBarrier2& buffer_barrier, - VkDependencyFlags dependency_flags = 0) const noexcept { - PipelineBarrier(dependency_flags, {}, buffer_barrier, {}); + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependency_flags, + const VkBufferMemoryBarrier& buffer_barrier) const noexcept { + PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, {}, buffer_barrier, {}); } - void PipelineBarrier(const VkImageMemoryBarrier2& image_barrier, - VkDependencyFlags dependency_flags = 0) const noexcept { - PipelineBarrier(dependency_flags, {}, {}, image_barrier); + void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependency_flags, + const VkImageMemoryBarrier& image_barrier) const noexcept { + PipelineBarrier(src_stage_mask, dst_stage_mask, dependency_flags, {}, {}, image_barrier); } void BindDescriptorBuffersEXT(Span bindings) const noexcept { @@ -1396,59 +1474,26 @@ public: } void CopyBufferToImage(VkBuffer src_buffer, VkImage dst_image, VkImageLayout dst_image_layout, - Span regions) const noexcept { - const VkCopyBufferToImageInfo2 info{ - .sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2, - .pNext = nullptr, - .srcBuffer = src_buffer, - .dstImage = dst_image, - .dstImageLayout = dst_image_layout, - .regionCount = regions.size(), - .pRegions = regions.data(), - }; - dld->vkCmdCopyBufferToImage2(handle, &info); + Span regions) const noexcept { + dld->vkCmdCopyBufferToImage(handle, src_buffer, dst_image, dst_image_layout, regions.size(), + regions.data()); } void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, - Span regions) const noexcept { - const VkCopyBufferInfo2 info{ - .sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2, - .pNext = nullptr, - .srcBuffer = src_buffer, - .dstBuffer = dst_buffer, - .regionCount = regions.size(), - .pRegions = regions.data(), - }; - dld->vkCmdCopyBuffer2(handle, &info); + Span regions) const noexcept { + dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); } void CopyImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, - VkImageLayout dst_layout, Span regions) const noexcept { - const VkCopyImageInfo2 info{ - .sType = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2, - .pNext = nullptr, - .srcImage = src_image, - .srcImageLayout = src_layout, - .dstImage = dst_image, - .dstImageLayout = dst_layout, - .regionCount = regions.size(), - .pRegions = regions.data(), - }; - dld->vkCmdCopyImage2(handle, &info); + VkImageLayout dst_layout, Span regions) const noexcept { + dld->vkCmdCopyImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), + regions.data()); } void CopyImageToBuffer(VkImage src_image, VkImageLayout src_layout, VkBuffer dst_buffer, - Span regions) const noexcept { - const VkCopyImageToBufferInfo2 info{ - .sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2, - .pNext = nullptr, - .srcImage = src_image, - .srcImageLayout = src_layout, - .dstBuffer = dst_buffer, - .regionCount = regions.size(), - .pRegions = regions.data(), - }; - dld->vkCmdCopyImageToBuffer2(handle, &info); + Span regions) const noexcept { + dld->vkCmdCopyImageToBuffer(handle, src_image, src_layout, dst_buffer, regions.size(), + regions.data()); } void CopyQueryPoolResults(VkQueryPool query_pool, u32 first_query, u32 query_count, @@ -1518,6 +1563,19 @@ public: dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); } + void SetEvent(VkEvent event, VkPipelineStageFlags stage_flags) const noexcept { + dld->vkCmdSetEvent(handle, event, stage_flags); + } + + void WaitEvents(Span events, VkPipelineStageFlags src_stage_mask, + VkPipelineStageFlags dst_stage_mask, Span memory_barriers, + Span buffer_barriers, + Span image_barriers) const noexcept { + dld->vkCmdWaitEvents(handle, events.size(), events.data(), src_stage_mask, dst_stage_mask, + memory_barriers.size(), memory_barriers.data(), buffer_barriers.size(), + buffer_barriers.data(), image_barriers.size(), image_barriers.data()); + } + void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers, const VkDeviceSize* offsets, const VkDeviceSize* sizes, const VkDeviceSize* strides) const noexcept { @@ -1568,6 +1626,11 @@ public: dld->vkCmdSetLineStippleEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); } + void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept + { + dld->vkCmdSetLineStippleEXT(handle, factor, pattern); + } + void SetDepthBiasEnableEXT(bool enable) const noexcept { dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); } @@ -1596,6 +1659,10 @@ public: dld->vkCmdSetLogicOpEXT(handle, logic_op); } + void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept { + dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points); + } + void SetColorWriteMaskEXT(u32 first, Span masks) const noexcept { dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data()); } @@ -1666,6 +1733,20 @@ public: dld->vkCmdEndConditionalRenderingEXT(handle); } + void BeginDebugUtilsLabelEXT(const char* label, std::span color) const noexcept { + const VkDebugUtilsLabelEXT label_info{ + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pNext = nullptr, + .pLabelName = label, + .color{color[0], color[1], color[2], color[3]}, + }; + dld->vkCmdBeginDebugUtilsLabelEXT(handle, &label_info); + } + + void EndDebugUtilsLabelEXT() const noexcept { + dld->vkCmdEndDebugUtilsLabelEXT(handle); + } + private: VkCommandBuffer handle; const DeviceDispatch* dld; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7affda9c56..308db3621b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -157,7 +157,7 @@ GRenderWindow::~GRenderWindow() { input_subsystem->Shutdown(); } -void GRenderWindow::OnFrameDisplayed([[maybe_unused]] u32 presented_frames) { +void GRenderWindow::OnFrameDisplayed() { input_subsystem->GetTas()->UpdateThread(); const InputCommon::TasInput::TasState new_tas_state = std::get<0>(input_subsystem->GetTas()->GetStatus()); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 85218f4cb4..bb18bbb3bb 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -67,7 +67,7 @@ public: ~GRenderWindow() override; // EmuWindow implementation. - void OnFrameDisplayed(u32 presented_frames) override; + void OnFrameDisplayed() override; bool IsShown() const override; std::unique_ptr CreateSharedContext() const override; diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h index 54f494d2f1..b794b49e0b 100644 --- a/src/yuzu/configuration/configure_applets.h +++ b/src/yuzu/configuration/configure_applets.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,6 @@ class QCheckBox; class QLineEdit; -class QComboBox; class QDateTimeEdit; namespace Core { class System; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 15b24c5a1b..641f65d82a 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -41,37 +41,6 @@ #include "yuzu/configuration/configure_post_processing.h" #endif -#ifdef HAS_LSFG -#include -#include -#include -#include -#include -#include -#include "common/string_util.h" -#include "video_core/frame_gen/lossless_dll.h" - -namespace { - -QString LosslessSearchDirectory() { - const QString suffix = QStringLiteral("/steamapps/common/Lossless Scaling"); - const std::array roots{ - QDir::homePath() + QStringLiteral("/.local/share/Steam"), - QDir::homePath() + QStringLiteral("/.steam/steam"), - QDir::homePath() + QStringLiteral("/.var/app/com.valvesoftware.Steam/.local/share/Steam"), - QStringLiteral("C:/Program Files (x86)/Steam"), - }; - for (const QString& root : roots) { - if (QDir(root + suffix).exists()) { - return root + suffix; - } - } - return QDir::homePath(); -} - -} // Anonymous namespace -#endif - ConfigureGraphics::ConfigureGraphics( const Core::System& system_, std::vector& records_, const std::function& expose_compute_option_, @@ -353,27 +322,11 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) { hold_graphics.emplace(setting->Id(), widget); #endif -#ifdef HAS_LSFG - } else if (setting->Id() == Settings::values.frame_gen.Id()) { - SetupFrameGen(widget); - hold_graphics.emplace(setting->Id(), widget); - } else if (setting->PairedSetting() == &Settings::values.frame_gen) { - frame_gen_widgets.push_back(widget); - hold_graphics.emplace(setting->Id(), widget); -#endif } else { hold_graphics.emplace(setting->Id(), widget); } } -#ifdef HAS_LSFG - if (frame_gen_checkbox != nullptr) { - for (QWidget* frame_gen_widget : frame_gen_widgets) { - frame_gen_widget->setVisible(frame_gen_checkbox->isChecked()); - } - } -#endif - for (const auto& [id, widget] : hold_graphics) { graphics_layout.addWidget(widget); } @@ -558,84 +511,3 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { return Settings::RendererBackend::OpenGL_GLSL; return selected_backend; } - -#ifdef HAS_LSFG -void ConfigureGraphics::SetupFrameGen(ConfigurationShared::Widget* widget) { - frame_gen_checkbox = widget->checkbox; - lossless_button = new QPushButton(widget); - lossless_button->setEnabled(!system.IsPoweredOn()); - UpdateLosslessButton(); - - connect(lossless_button, &QAbstractButton::clicked, this, [this] { InstallLosslessDll(); }); - connect(frame_gen_checkbox, &QCheckBox::toggled, this, [this](bool checked) { - for (QWidget* frame_gen_widget : frame_gen_widgets) { - frame_gen_widget->setVisible(checked); - } - }); - - QBoxLayout* row = qobject_cast(widget->layout()); - row->insertWidget(1, lossless_button); -} - -void ConfigureGraphics::UpdateLosslessButton() { - using VideoCore::FrameGen::LosslessStatus; - if (VideoCore::FrameGen::GetInstalledLosslessStatus() == LosslessStatus::Ok) { - lossless_button->setText(tr("Replace Lossless.dll...")); - } else { - lossless_button->setText(tr("Install Lossless.dll...")); - } -} - -void ConfigureGraphics::InstallLosslessDll() { - using VideoCore::FrameGen::LosslessStatus; - const auto status_text = [](LosslessStatus status) { - switch (status) { - case LosslessStatus::NotPortableExecutable: - return tr("The selected file is not a Windows library. Select Lossless.dll from your " - "Lossless Scaling installation."); - case LosslessStatus::MissingShaders: - return tr("This copy of Lossless.dll does not contain the frame generation shaders. " - "Update Lossless Scaling and try again."); - case LosslessStatus::TranslationFailed: - return tr("The frame generation shaders could not be translated. This version of " - "Lossless Scaling is not supported yet."); - case LosslessStatus::CacheUnusable: - return tr("The translated shaders could not be written to storage. Check that there " - "is free space available."); - default: - return tr("The selected file could not be read."); - } - }; - - const QString file = - QFileDialog::getOpenFileName(this, tr("Select Lossless.dll"), LosslessSearchDirectory(), - tr("Lossless Scaling library (Lossless.dll)")); - if (file.isEmpty()) { - return; - } - - const std::filesystem::path source{Common::U16StringFromBuffer(file.utf16(), file.size())}; - LosslessStatus status = VideoCore::FrameGen::ValidateLosslessDll(source); - if (status == LosslessStatus::Ok) { - const std::filesystem::path target = VideoCore::FrameGen::GetLosslessDllPath(); - std::error_code error; - std::filesystem::create_directories(target.parent_path(), error); - std::filesystem::copy_file(source, target, - std::filesystem::copy_options::overwrite_existing, error); - if (error) { - QMessageBox::warning(this, tr("Could not install Lossless.dll"), - tr("The selected file could not be copied.")); - return; - } - status = VideoCore::FrameGen::BuildShaderCache(); - } - - UpdateLosslessButton(); - if (status == LosslessStatus::Ok) { - QMessageBox::information(this, tr("Lossless Scaling"), - tr("Lossless.dll installed successfully.")); - return; - } - QMessageBox::warning(this, tr("Could not install Lossless.dll"), status_text(status)); -} -#endif diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 95fe1995cb..9002af437b 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -22,7 +22,6 @@ #include "qt_common/util/vk.h" #include "yuzu/configuration/configuration_shared.h" -class QCheckBox; class QPushButton; class QEvent; class QObject; @@ -43,7 +42,6 @@ class ConfigureGraphics; namespace ConfigurationShared { class Builder; -class Widget; } class ConfigureGraphics : public ConfigurationShared::Tab { @@ -85,12 +83,6 @@ private: int FindIndex(u32 enumeration, int value) const; -#ifdef HAS_LSFG - void SetupFrameGen(ConfigurationShared::Widget* widget); - void InstallLosslessDll(); - void UpdateLosslessButton(); -#endif - std::unique_ptr ui; QColor bg_color; @@ -118,9 +110,4 @@ private: QWidget* api_widget; QComboBox* aspect_ratio_combobox; QComboBox* resolution_combobox; -#ifdef HAS_LSFG - QCheckBox* frame_gen_checkbox{}; - QPushButton* lossless_button{}; - std::vector frame_gen_widgets; -#endif }; diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index 5dd6094b96..5e8e04be58 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -140,7 +140,12 @@ QWidget* Widget::CreateCombobox(std::function& serializer, if (combobox_enumerations.contains(type)) { enumeration = &combobox_enumerations.at(type); for (const auto& [id, name] : *enumeration) { - combobox->addItem(name); + if (type == Settings::EnumMetadata::Index() && + id == static_cast(Settings::AppletMode::Disabled) && + setting.Id() != Settings::values.controller_applet_mode.Id()) { + continue; + } + combobox->addItem(name, id); } } else { return combobox; @@ -158,9 +163,8 @@ QWidget* Widget::CreateCombobox(std::function& serializer, const u32 setting_value = std::strtoul(setting.ToString().c_str(), nullptr, 0); combobox->setCurrentIndex(find_index(setting_value)); - serializer = [this, enumeration]() { - int current = combobox->currentIndex(); - return std::to_string(enumeration->at(current).first); + serializer = [this]() { + return std::to_string(combobox->currentData().toUInt()); }; restore_func = [this, find_index]() { @@ -511,12 +515,7 @@ void Widget::SetupComponent(const QString& label, std::function& load_fu layout->setContentsMargins(0, 0, 0, 0); if (other_setting == nullptr) { - Settings::BasicSetting* const paired = setting.PairedSetting(); - const bool paired_standalone = paired != nullptr && translations.contains(paired->Id()) && - !translations.at(paired->Id()).first.isEmpty(); - if (!paired_standalone) { - other_setting = paired; - } + other_setting = setting.PairedSetting(); } const bool require_checkbox = other_setting != nullptr && other_setting->TypeId() == "bool"; @@ -779,12 +778,7 @@ Builder::~Builder() = default; static bool IsAndroidOnly(const Settings::BasicSetting& setting) { const std::string& label = setting.GetLabel(); -#ifndef HAS_LSFG - if (label.starts_with("frame_gen")) { - return true; - } -#endif - return label == "emulate_bgr565"; + return label.starts_with("frame_gen") || label == "emulate_bgr565"; } Widget* Builder::BuildWidget(Settings::BasicSetting* setting, diff --git a/src/yuzu/game/common.h b/src/yuzu/game/common.h index 03505d6313..063733e8d8 100644 --- a/src/yuzu/game/common.h +++ b/src/yuzu/game/common.h @@ -12,25 +12,29 @@ namespace Yuzu { inline bool ContainsAllWords(const QString& haystack, const QString& userinput) { const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts); - return std::all_of(userinput_split.begin(), userinput_split.end(), - [&haystack](const QString& s) { return haystack.contains(s); }); + return std::all_of(userinput_split.begin(), userinput_split.end(), [&haystack](const QString& s) { + return haystack.contains(s); + }); } inline bool FilterMatches(const QString& filter, const QStandardItem* item) { if (filter.isEmpty()) return true; + auto const sluggify = [](const QString& s) { + QString o(s.normalized(QString::NormalizationForm_D).toLower()); + return o.replace(QRegularExpression(QStringLiteral("[^a-z\\s]")), QStringLiteral("")); + }; + + const auto norm_filter = sluggify(filter); const auto program_id = item->data(GameListItemPath::ProgramIdRole).toULongLong(); - - const QString file_path = item->data(GameListItemPath::FullPathRole).toString().toLower(); - const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower(); + const QString file_path = sluggify(item->data(GameListItemPath::FullPathRole).toString()); + const QString file_title = sluggify(item->data(GameListItemPath::TitleRole).toString()); const QString file_program_id = QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'}); - - const QString file_name = - file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title; - - return Yuzu::ContainsAllWords(file_name, filter) || - (file_program_id.size() == 16 && file_program_id.contains(filter)); + const QString file_name = sluggify(file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title); + return (ContainsAllWords(file_name, norm_filter) + || ContainsAllWords(file_title, norm_filter)) + || (file_program_id.size() == 16 && file_program_id.contains(norm_filter)); } } // namespace Yuzu diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 1d579239a1..ad76e5b2db 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -3,6 +3,7 @@ // Static Qt on macOS doesn't use Vulkan // Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols +#include "core/launch_params.h" #if defined(QT_STATICPLUGIN) && !defined(__APPLE__) #undef VMA_IMPLEMENTATION #endif @@ -515,79 +516,28 @@ MainWindow::MainWindow(bool has_broken_vulkan) // to prevent the UI from blowing up. UpdateUITheme(); - QStringList args = QApplication::arguments(); + if (QStringList args = QApplication::arguments(); args.size() >= 2) { + QList qba{}; + for (auto const& e : args) + qba.push_back(e.toUtf8()); + std::vector args_cstr{}; + for (auto& e : qba) + args_cstr.push_back(e.data()); - if (args.size() < 2) { - return; - } + auto const lp = Core::ParseLaunchParams(*QtCommon::system, args_cstr.size(), args_cstr.data(), nullptr); - QString game_path; - bool should_launch_qlaunch = false; - bool should_launch_hlaunch = false; - bool should_launch_setup = false; - bool has_gamepath = false; - bool is_fullscreen = false; - - // Preserves drag/drop functionality - for (int i = 1; i < args.size(); ++i) { - if (args[i] == QStringLiteral("-f")) { - // Launch game in fullscreen mode - is_fullscreen = true; - } else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) { - // Launch game with a specific user - int user_arg_idx = ++i; - bool argument_ok; - std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok); - if (!argument_ok) { - // try to look it up by username, only finds the first username that matches. - std::string const user_arg_str = args[user_arg_idx].toStdString(); - auto const user_idx = - QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str); - if (user_idx != std::nullopt) { - selected_user = user_idx.value(); - } else { - LOG_ERROR(Frontend, "Invalid user argument '{}'", user_arg_str); - continue; - } - } - if (QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) { - Settings::values.current_user = s32(selected_user); - user_flag_cmd_line = true; - } else { - LOG_ERROR(Frontend, "Selected user {} doesn't exist", selected_user); - } - } else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) { - // Launch game at path - game_path = args[++i]; - has_gamepath = true; - } else if (args[i] == QStringLiteral("-input-profile") && i < args.size() - 1) { - auto& players = Settings::values.players.GetValue(); - players[0].profile_name = args[++i].toStdString(); - } else if (args[i] == QStringLiteral("-qlaunch")) { - should_launch_qlaunch = true; - } else if (args[i] == QStringLiteral("-hlaunch")) { - should_launch_hlaunch = true; - } else if (args[i] == QStringLiteral("-setup")) { - should_launch_setup = true; - } else { - game_path = args[i]; - has_gamepath = true; + // Override fullscreen setting if gamepath or argument is provided + if (!lp.filepath.empty() || lp.fullscreen) { + ui->action_Fullscreen->setChecked(lp.fullscreen); } - } - // Override fullscreen setting if gamepath or argument is provided - if (has_gamepath || is_fullscreen) { - ui->action_Fullscreen->setChecked(is_fullscreen); - } - - if (should_launch_setup) { - LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt); - } else { - if (!game_path.isEmpty()) { - BootGame(game_path, ApplicationAppletParameters()); - } else if (should_launch_qlaunch) { + if (!lp.filepath.empty()) { + BootGame(QString::fromStdString(lp.filepath), ApplicationAppletParameters()); + } else if (lp.launch_setup) { + LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt); + } else if (lp.launch_qlaunch) { LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); - } else if (should_launch_hlaunch) { + } else if (lp.launch_hlaunch) { std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string(); @@ -1836,7 +1786,7 @@ bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPar QtCommon::system->SetFrontendAppletSet({ std::make_unique(*this), // Amiibo Settings - (UISettings::values.controller_applet_disabled.GetValue() == true) + (Settings::values.controller_applet_mode.GetValue() == Settings::AppletMode::Disabled) ? nullptr : std::make_unique(*this), // Controller Selector std::make_unique(*this), // Error Display @@ -2847,9 +2797,9 @@ void MainWindow::IncrementInstallProgress() { void MainWindow::OnMenuInstallToNAND() { const QString file_filter = - tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " - "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " - "Image (*.xci)"); + tr("Installable Switch File (*.nca *.nsp *.nsz *.xci *.xcz);;Nintendo Content Archive " + "(*.nca);;Nintendo Submission Package (*.nsp *.nsz);;NX Cartridge " + "Image (*.xci *.xcz)"); QStringList filenames = QFileDialog::getOpenFileNames( this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path), @@ -2906,7 +2856,10 @@ void MainWindow::OnMenuInstallToNAND() { QFuture future; ContentManager::InstallResult result; - if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { + if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive) || + file.endsWith(QStringLiteral("nsz"), Qt::CaseInsensitive) || + file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || + file.endsWith(QStringLiteral("xcz"), Qt::CaseInsensitive)) { const auto progress_callback = [this](size_t size, size_t progress) { emit UpdateInstallProgress(); if (install_progress->wasCanceled()) { @@ -3711,12 +3664,12 @@ void MainWindow::OnToggleGraphicsAPI() { api = Settings::RendererBackend::OpenGL_GLSL; break; case Settings::RendererBackend::OpenGL_GLSL: - api = Settings::RendererBackend::OpenGL_GLSL; - break; - case Settings::RendererBackend::OpenGL_SPIRV: api = Settings::RendererBackend::OpenGL_GLASM; break; case Settings::RendererBackend::OpenGL_GLASM: + api = Settings::RendererBackend::OpenGL_SPIRV; + break; + case Settings::RendererBackend::OpenGL_SPIRV: api = Settings::RendererBackend::Null; break; #else diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 09a9bd6da1..17a1545167 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -42,9 +42,6 @@ target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common if (ENABLE_OPENGL) target_link_libraries(yuzu-cmd PRIVATE glad) endif() -if (MSVC) - target_link_libraries(yuzu-cmd PRIVATE getopt) -endif() target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index a161418289..473a5b1014 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -10,6 +10,7 @@ #include #include #include "common/settings_enums.h" +#include "core/launch_params.h" #ifdef __EMSCRIPTEN__ #include #endif @@ -52,12 +53,6 @@ #include "common/windows/timer_resolution.h" #endif -#undef _UNICODE -#include -#ifndef _MSC_VER -#include -#endif - #ifdef _WIN32 extern "C" { // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable @@ -67,25 +62,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif -static void PrintHelp(const char* argv0) { - std::cout << "Usage: " << argv0 - << " [options] \n" - "-c, --config Load the specified configuration file\n" - "-f, --fullscreen Start in fullscreen mode\n" - "-g, --game File path of the game to load\n" - "-h, --help Display this help and exit\n" - "-m, --multiplayer=nick:password@address:port" - " Nickname, password, address and port for multiplayer\n" - "-p, --program Pass following string as arguments to executable\n" - "-u, --user Select a specific user profile from 0 to 7\n" - "-d, --debug Run the GDB stub on a port from 1 to 65535\n" - "-v, --version Output version information and exit\n"; -} - -static void PrintVersion() { - std::cout << "Eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; -} - static void OnStateChanged(const Network::RoomMember::State& state) { switch (state) { case Network::RoomMember::State::Idle: @@ -208,168 +184,18 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { LOG_CRITICAL(Frontend, "Failed to get command line arguments"); return SDL_APP_FAILURE; } -#endif - std::string filepath; - std::optional config_path{}; - std::string program_args; - std::optional selected_user{}; - std::optional override_gdb_port{}; - bool use_multiplayer = false; - bool fullscreen = false; - bool force_null_render = false; - bool force_single_core = false; - std::string nickname{}; - std::string password{}; - std::string address{}; - std::string input_profile{}; - std::optional log_filter{}; - u16 port = Network::DefaultRoomPort; - - static struct option long_options[] = { - // clang-format off - {"debug", no_argument, 0, 'd'}, - {"config", required_argument, 0, 'c'}, - {"fullscreen", no_argument, 0, 'f'}, - {"help", no_argument, 0, 'h'}, - {"game", required_argument, 0, 'g'}, - {"multiplayer", required_argument, 0, 'm'}, - {"program", optional_argument, 0, 'p'}, - {"user", required_argument, 0, 'u'}, - {"version", no_argument, 0, 'v'}, - {"input-profile", no_argument, 0, 'i'}, - {"null-render", no_argument, 0, 'n'}, - {"singlecore", no_argument, 0, 's'}, - {"filter", no_argument, 0, 'x'}, - {0, 0, 0, 0}, - // clang-format on - }; - - while (optind < argc) { - int arg = getopt_long(argc, argv, "g:fhvcip::c:u:d:", long_options, &option_index); - if (arg != -1) { - switch (char(arg)) { - case 'd': - override_gdb_port = uint16_t(atoi(optarg)); - break; - case 'c': - config_path = optarg; - break; - case 'f': - fullscreen = true; - LOG_INFO(Frontend, "Starting in fullscreen mode..."); - break; - case 'h': - PrintHelp(argv[0]); - return SDL_APP_FAILURE; - case 'g': - filepath = std::string(optarg); - break; - case 'i': { - input_profile = std::string(optarg); - break; - } - case 'm': { - use_multiplayer = true; - const std::string str_arg(optarg); - // regex to check if the format is nickname:password@ip:port - // with optional :password - const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); - if (!std::regex_match(str_arg, re)) { - std::cout << "Wrong format for option --multiplayer\n"; - PrintHelp(argv[0]); - return SDL_APP_FAILURE; - } - - std::smatch match; - std::regex_search(str_arg, match, re); - ASSERT(match.size() == 5); - nickname = match[1]; - password = match[2]; - address = match[3]; - if (!match[4].str().empty()) { - port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0)); - } - std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); - if (!std::regex_match(nickname, nickname_re)) { - LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters"); - return SDL_APP_FAILURE; - } - if (address.empty()) { - LOG_ERROR(Frontend, "Address to room must not be empty"); - return SDL_APP_FAILURE; - } - break; - } - case 'p': - program_args = argv[optind]; - ++optind; - break; - case 'u': - selected_user = atoi(optarg); - break; - case 'v': - PrintVersion(); - return SDL_APP_FAILURE; - case 'n': - force_null_render = true; - break; - case 's': - force_single_core = true; - break; - case 'x': - log_filter = argv[optind]; - ++optind; - break; - } - } else { -#ifdef _WIN32 - filepath = Common::UTF16ToUTF8(argv_w[optind]); #else - filepath = argv[optind]; + wchar_t **argv_w = nullptr; #endif - optind++; - } - } - - SdlConfig config{config_path}; - - // apply the log_filter setting - // the logger was initialized before and doesn't pick up the filter on its own - Common::Log::Filter filter; - filter.ParseFilterString(log_filter.value_or(Settings::values.log_filter.GetValue())); - Common::Log::SetGlobalFilter(filter); - - if (!program_args.empty()) { - Settings::values.program_args = program_args; - } - - if (!input_profile.empty()) { - auto& players = Settings::values.players.GetValue(); - players[0].profile_name = input_profile; - } - - if (selected_user.has_value()) { - Settings::values.current_user = std::clamp(*selected_user, 0, 7); - } - - if (override_gdb_port.has_value()) { - Settings::values.use_gdbstub = true; - Settings::values.gdbstub_port = *override_gdb_port; - } - - if (force_single_core) { - Settings::values.use_multi_core = false; - } - - if (force_null_render) { - Settings::values.renderer_backend = Settings::RendererBackend::Null; - } - + Core::LaunchParams lp = Core::ParseLaunchParams(state->system, argc, argv, argv_w); #ifdef _WIN32 LocalFree(argv_w); #endif + SdlConfig config{lp.config_path}; - if (filepath.empty()) { + Core::ApplyLaunchParams(lp); + + if (lp.filepath.empty()) { LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); return SDL_APP_FAILURE; } @@ -386,14 +212,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { case Settings::RendererBackend::OpenGL_GLSL: case Settings::RendererBackend::OpenGL_GLASM: case Settings::RendererBackend::OpenGL_SPIRV: - state->emu_window = std::make_unique(&input_subsystem, state->system, fullscreen); + state->emu_window = std::make_unique(&input_subsystem, state->system, lp.fullscreen); break; #endif case Settings::RendererBackend::Vulkan: - state->emu_window = std::make_unique(&input_subsystem, state->system, fullscreen); + state->emu_window = std::make_unique(&input_subsystem, state->system, lp.fullscreen); break; case Settings::RendererBackend::Null: - state->emu_window = std::make_unique(&input_subsystem, state->system, fullscreen); + state->emu_window = std::make_unique(&input_subsystem, state->system, lp.fullscreen); break; default: LOG_CRITICAL(Frontend, "Invalid renderer backend"); @@ -413,12 +239,12 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { Service::AM::FrontendAppletParameters load_parameters{ .applet_id = Service::AM::AppletId::Application, }; - const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters); + const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, lp.filepath, load_parameters); switch (load_result) { case Core::SystemResultStatus::Success: break; // Expected case case Core::SystemResultStatus::ErrorGetLoader: - LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath); + LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", lp.filepath); return SDL_APP_FAILURE; case Core::SystemResultStatus::ErrorLoader: LOG_CRITICAL(Frontend, "Failed to load ROM!"); @@ -440,14 +266,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { return SDL_APP_FAILURE; } - if (use_multiplayer) { + if (lp.use_multiplayer) { if (auto member = Network::GetRoomMember().lock()) { member->BindOnChatMessageReceived(OnMessageReceived); member->BindOnStatusMessageReceived(OnStatusMessageReceived); member->BindOnStateChanged(OnStateChanged); member->BindOnError(OnNetworkError); - LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname); - member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); + LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", lp.address, lp.port, lp.nickname); + member->Join(lp.nickname, lp.address.c_str(), lp.port, 0, Network::NoPreferredIP, lp.password); } else { LOG_ERROR(Network, "Could not access RoomMember"); return SDL_APP_FAILURE;