Compare commits

...

25 Commits

Author SHA1 Message Date
gdkchan
f1d1670b0b Implement HLE macro for DrawElementsIndirect (#3748)
* Implement HLE macro for DrawElementsIndirect

* Shader cache version bump

* Use GL_ARB_shader_draw_parameters extension on OpenGL

* Fix DrawIndexedIndirectCount on Vulkan when extension is not supported

* Implement DrawIndex

* Alignment

* Fix some validation errors

* Rename BaseIds to DrawParameters

* Fix incorrect index buffer and vertex buffer size in some cases

* Add HLE macros for DrawArraysInstanced and DrawElementsInstanced

* Perform a regular draw when indirect data is not modified

* Use non-indirect draw methods if indirect buffer was not GPU modified

* Only check if draw parameters match if the shader actually uses them

* Expose Macro HLE setting on GUI

* Reset FirstVertex and FirstInstance after draw

* Update shader cache version again since some people already tested this

* PR feedback

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2022-11-16 14:53:04 -03:00
EmulationFanatic
b8de72de8f It's REE-YOU-JINX (#3839) 2022-11-15 22:19:25 +01:00
TSRBerry
eebc39228d UI: Allow overriding graphics backend + Move command line parser into a new class (#3707)
* Ava: Keep command line args when restarting

* UI: Move common UI functions to ProgramHelper

Add command line option to override the configured graphics backend

* Ava: Add CleanupUpdate task back

* Remove unused usings

* Revert combining common UI functions

Rename ProgramHelper to CommandLineState
Move command line parsing to CommandLineState

* Rename CommandLineProfile to Profile

* Fix assigning the wrong array to Arguments
2022-11-12 20:36:36 -03:00
gdkchan
9daf029f35 Use vector transform feedback outputs if possible (#3832) 2022-11-12 20:20:40 -03:00
gdkchan
51a27032f0 Fix VertexId and InstanceId on Vulkan (#3833)
* Fix VertexId and InstanceId on Vulkan

* Shader cache version bump
2022-11-11 13:22:49 -03:00
gdkchan
a6a67a2b7a Minor improvement to Vulkan pipeline state and bindings management (#3829)
* Minor improvement to Vulkan pipeline state and bindings management

* Clean up buffer textures too

* Use glBindTextureUnit
2022-11-10 13:38:38 -03:00
Mary-nyan
c6d05301aa infra: Migrate to .NET 7 (#3795)
* Update readme to mention .NET 7

* infra: Migrate to .NET 7

.NET 7 is still in preview but this prepare for the release coming up
next month.

* Use Random.Shared in CreateRandom

* Move UInt128Utils.cs to Ryujinx.Common project

* Fix inverted parameters in System.UInt128 constructor

* Fix Visual Studio complains on  Ryujinx.Graphics.Vic

* time: Fix missing alignment enforcement in SystemClockContext

Fixes at least Smash

* time: Fix missing alignment enforcement in SteadyClockContext

Fix games (like recent version of Smash) using time shared memory

* Switch to .NET 7.0.100 release

* Enable Tiered PGO

* Ensure CreateId validity requirements are meet when doing random generation

Also enforce correct packing layout for other Mii structures.

This fix a Mario Kart 8 crashes related to the default Miis.
2022-11-09 20:22:43 +01:00
gdkchan
647de4cd31 Ensure all pending draws are done before compute dispatch (#3822) 2022-11-03 19:54:30 -03:00
gdkchan
f82309fa2d Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve (#3723)
* Vulkan: Implement multisample <-> non-multisample copies and depth-stencil resolve

* FramebufferParams is no longer required there

* Implement Specialization Constants and merge CopyMS Shaders (#15)

* Vulkan: Initial Specialization Constants

* Replace with specialized helper shader

* Reimplement everything

Fix nonexistant interaction with Ryu pipeline caching
Decouple specialization info from data and relocate them
Generalize mapping and add type enum to better match spv types
Use local fixed scopes instead of global unmanaged allocs

* Fix misses in initial implementation

Use correct info variable in Create2DLayerView
Add ShaderStorageImageMultisample to required feature set

* Use texture for source image

* No point in using ReadOnlyMemory

* Apply formatting feedback

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply formatting suggestions on shader source

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Support conversion with samples count that does not match the requested count, other minor changes

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>
2022-11-02 18:17:19 -03:00
Mary-nyan
7d8e198c33 fix: Support FFmpeg 5.1.x for decoding (#3816)
For some reason FFmpeg 5.1.x reverted part of the changes made in 5.0.x
on AVCodec.

This fix decoding issues with it.
2022-11-02 09:26:50 +01:00
riperiperi
3d98e1361b GPU: Use a bitmap to track buffer modified flags. (#3775)
* Initial implementation

* Some improvements.

* Fix incorrect cast

* Performance improvement and improved correctness

* Add very fast path when all handles are checked.

* Slightly faster

* Add comment

* De-virtualize region handle

All region handles are now bitmap backed.

* Remove non-bitmap tracking

* Remove unused methods

* Add docs, remove unused methods

* Address Feedback

* Rename file
2022-10-29 22:07:37 +00:00
TSRBerry
141cf61ff7 CI: Run git_short_hash inside of bash (#3808) 2022-10-29 19:00:08 +00:00
Wunk
3fe3598d41 Vulkan: Replace VK_EXT_debug_report usage with VK_EXT_debug_utils (#3802)
* Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils`

[VK_EXT_debug_report](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_report.html)
has been depreciated for quite some time now in favor of the much more
featureful
[VK_EXT_debug_utils](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html)
extension.

This PR converts our debug-report-callback into the newer
debug-messenger pattern.

`VK_EXT_debug_utils` adds some additional diagnostic tooling for marking
debug-label scopes for queue-operations, command-buffers, and assigning
name-labels to vulkan objects to aid in debugging(for a later PR).

* Vulkan: Fix `DebugMessenger` severity-flag classification

Extension bits between the two flags, for reference:

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugUtilsMessageSeverityFlagBitsEXT.html

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugReportFlagBitsEXT.html
2022-10-29 14:09:25 -03:00
gdkchan
59cdf310bd SPIR-V: Fix tessellation control shader output types (#3807)
* SPIR-V: Fix tessellation control shader output types

* Shader cache version bump
2022-10-29 13:45:30 -03:00
dependabot[bot]
4e34170a84 nuget: bump System.IdentityModel.Tokens.Jwt from 6.15.0 to 6.25.0 (#3806)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.25.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-29 10:46:46 +02:00
Luke Warner
d540af5dc0 AppletAE: stub SetRecordVolumeMuted (#3804)
* Update IIrSensorServer.cs

* Update IIrSensorServer.cs

* Apply suggestions from code review

Addressed formatting feedback

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update IIrSensorServer.cs

* Update ISelfController.cs

* Update ISelfController.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-10-27 23:15:57 +00:00
Luke Warner
f7c7b66fc0 hid/irs: Stub StopImageProcessorAsync (#3799)
* Update IIrSensorServer.cs

* Update IIrSensorServer.cs

* Apply suggestions from code review

Addressed formatting feedback

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update IIrSensorServer.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-10-27 21:36:37 +00:00
gdkchan
28ba55598d Vulkan: Fix indirect buffer barrier (#3798) 2022-10-26 14:53:11 -03:00
riperiperi
9719b6a112 Vulkan: Use dynamic state for blend constants (#3793) 2022-10-25 23:49:23 +00:00
VocalFan
f70236f947 Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression) (#3568)
* Updated Compatibility info and GPU info (Vulkan, SPIRV, and Texture Recompression)

* Added Mutant's changes.

Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com>

* Five to four.

* Fixed github's terrible conflict diffs

Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com>
2022-10-24 16:40:39 +00:00
merry
eafadf10c7 Ryujinx.Tests.Unicorn: Implement IDisposable (#3794)
Dispose unicorn when done
2022-10-23 23:51:54 +00:00
Mary-nyan
9b06ee7736 Attempt to fix issues since github-script v6 upgrade 2022-10-23 17:15:15 +02:00
Emmanuel Hansen
baba2c2467 Avalonia: Use overlay dialog for controller applet (#3777)
* use overlay dialog for controller applet

* Update Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2022-10-23 11:15:45 +02:00
dependabot[bot]
286e5d39b2 nuget: bump SPB from 0.0.4-build24 to 0.0.4-build27 (#3791)
Bumps [SPB](https://github.com/Thog/SPB) from 0.0.4-build24 to 0.0.4-build27.
- [Release notes](https://github.com/Thog/SPB/releases)
- [Commits](https://github.com/Thog/SPB/commits)

---
updated-dependencies:
- dependency-name: SPB
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-22 10:32:12 +02:00
TSRBerry
dc529c1181 ci: Add updates for nuget packages to dependabot (#3786) 2022-10-21 14:23:16 +02:00
178 changed files with 4953 additions and 1325 deletions

View File

@@ -10,3 +10,15 @@ updates:
- marysaka - marysaka
commit-message: commit-message:
prefix: "ci" prefix: "ci"
- package-ecosystem: nuget
directory: /
open-pull-requests-limit: 5
schedule:
interval: daily
labels:
- "infra"
reviewers:
- marysaka
commit-message:
prefix: nuget

View File

@@ -51,12 +51,13 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 6.0.x dotnet-version: 7.0.x
- name: Ensure NuGet Source - name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1 uses: fabriciomurta/ensure-nuget-source@v1
- name: Get git short hash - name: Get git short hash
id: git_short_hash id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Clear - name: Clear
run: dotnet clean && dotnet nuget locals all --clear run: dotnet clean && dotnet nuget locals all --clear
- name: Build - name: Build

View File

@@ -16,7 +16,7 @@ jobs:
const pull_head_sha = '${{github.event.workflow_run.head_sha}}'; const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await (async () => { const issue_number = await (async () => {
const pulls = await github.pulls.list({owner, repo}); const pulls = await github.rest.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) { for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) { for (const pull of data) {
if (pull.head.sha === pull_head_sha) { if (pull.head.sha === pull_head_sha) {
@@ -31,7 +31,7 @@ jobs:
return core.error(`No matching pull request found`); return core.error(`No matching pull request found`);
} }
const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id}); const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) { if (!artifacts.length) {
return core.error(`No artifacts found`); return core.error(`No artifacts found`);
} }
@@ -57,12 +57,12 @@ jobs:
body += hidden_headless_artifacts; body += hidden_headless_artifacts;
body += hidden_debug_artifacts; body += hidden_debug_artifacts;
const {data: comments} = await github.issues.listComments({repo, owner, issue_number}); const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]'); const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) { if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`); core.info(`Updating comment ${existing_comment.id}`);
await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body}); await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else { } else {
core.info(`Creating a comment`); core.info(`Creating a comment`);
await github.issues.createComment({repo, owner, issue_number, body}); await github.rest.issues.createComment({repo, owner, issue_number, body});
} }

View File

@@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 6.0.x dotnet-version: 7.0.x
- name: Ensure NuGet Source - name: Ensure NuGet Source
uses: fabriciomurta/ensure-nuget-source@v1 uses: fabriciomurta/ensure-nuget-source@v1
- name: Clear - name: Clear

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -36,8 +36,8 @@
## Compatibility ## Compatibility
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable.
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already! You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
## Usage ## Usage
@@ -62,7 +62,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
If you wish to build the emulator yourself, follow these steps: If you wish to build the emulator yourself, follow these steps:
### Step 1 ### Step 1
Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0). Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
### Step 2 ### Step 2
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files. Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
@@ -90,7 +90,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
- **GPU** - **GPU**
The GPU emulator emulates the Switch's Maxwell GPU using the OpenGL API (version 4.5 minimum) through a custom build of OpenTK. There are currently four graphics enhancements available to the end user in Ryujinx: disk shader caching, resolution scaling, aspect ratio adjustment and anisotropic filtering. These enhancements can be adjusted or toggled as desired in the GUI. The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
- **Input** - **Input**

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
</PropertyGroup> </PropertyGroup>

View File

@@ -150,7 +150,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary> /// </summary>
/// <param name="inputHeader">The splitter header.</param> /// <param name="inputHeader">The splitter header.</param>
/// <param name="input">The raw data after the splitter header.</param> /// <param name="input">The raw data after the splitter header.</param>
private void UpdateState(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input) private void UpdateState(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
{ {
for (int i = 0; i < inputHeader.SplitterCount; i++) for (int i = 0; i < inputHeader.SplitterCount; i++)
{ {
@@ -177,7 +177,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// </summary> /// </summary>
/// <param name="inputHeader">The splitter header.</param> /// <param name="inputHeader">The splitter header.</param>
/// <param name="input">The raw data after the splitter header.</param> /// <param name="input">The raw data after the splitter header.</param>
private void UpdateData(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input) private void UpdateData(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
{ {
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++) for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
{ {

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -10,6 +10,7 @@ using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -64,8 +65,7 @@ namespace Ryujinx.Ava
if (result == UserResult.Yes) if (result == UserResult.Yes)
{ {
var path = Process.GetCurrentProcess().MainModule.FileName; var path = Process.GetCurrentProcess().MainModule.FileName;
var info = new ProcessStartInfo() { FileName = path, UseShellExecute = false }; var proc = Process.Start(path, CommandLineState.Arguments);
var proc = Process.Start(info);
desktop.Shutdown(); desktop.Shutdown();
Environment.Exit(0); Environment.Exit(0);
} }

View File

@@ -587,10 +587,12 @@
"SettingsTabGraphicsPreferredGpu": "Preferred GPU", "SettingsTabGraphicsPreferredGpu": "Preferred GPU",
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
"SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied",
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?", "RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
"VolumeShort": "Vol" "VolumeShort": "Vol",
"SettingsEnableMacroHLE": "Enable Macro HLE",
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure."
} }

View File

@@ -556,5 +556,7 @@
"SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema", "SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema",
"SettingsXamlThemeFile" : "Arquivo de tema Xaml", "SettingsXamlThemeFile" : "Arquivo de tema Xaml",
"SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:", "SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:",
"SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:" "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:",
"SettingsEnableMacroHLE": "Habilitar emulação de alto nível para Macros",
"SettingsEnableMacroHLETooltip": "Habilita emulação de alto nível de códigos Macro da GPU.\n\nMelhora a performance, mas pode causar problemas gráficos em alguns jogos.\n\nEm caso de dúvida, deixe ATIVADO."
} }

View File

@@ -13,6 +13,7 @@ using Ryujinx.Common.SystemInfo;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -26,7 +27,6 @@ namespace Ryujinx.Ava
public static double ActualScaleFactor { get; set; } public static double ActualScaleFactor { get; set; }
public static string Version { get; private set; } public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; } public static string ConfigurationPath { get; private set; }
public static string CommandLineProfile { get; set; }
public static bool PreviewerDetached { get; private set; } public static bool PreviewerDetached { get; private set; }
public static RenderTimer RenderTimer { get; private set; } public static RenderTimer RenderTimer { get; private set; }
@@ -87,46 +87,8 @@ namespace Ryujinx.Ava
private static void Initialize(string[] args) private static void Initialize(string[] args)
{ {
// Parse Arguments. // Parse arguments
string launchPathArg = null; CommandLineState.ParseArguments(args);
string baseDirPathArg = null;
bool startFullscreenArg = false;
for (int i = 0; i < args.Length; ++i)
{
string arg = args[i];
if (arg == "-r" || arg == "--root-data-dir")
{
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
baseDirPathArg = args[++i];
}
else if (arg == "-p" || arg == "--profile")
{
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
CommandLineProfile = args[++i];
}
else if (arg == "-f" || arg == "--fullscreen")
{
startFullscreenArg = true;
}
else
{
launchPathArg = arg;
}
}
// Delete backup files after updating. // Delete backup files after updating.
Task.Run(Updater.CleanupUpdate); Task.Run(Updater.CleanupUpdate);
@@ -135,10 +97,10 @@ namespace Ryujinx.Ava
// Hook unhandled exception and process exit events. // Hook unhandled exception and process exit events.
AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
// Setup base data directory. // Setup base data directory.
AppDataManager.Initialize(baseDirPathArg); AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
// Initialize the configuration. // Initialize the configuration.
ConfigurationState.Initialize(); ConfigurationState.Initialize();
@@ -173,9 +135,9 @@ namespace Ryujinx.Ava
} }
} }
if (launchPathArg != null) if (CommandLineState.LaunchPathArg != null)
{ {
MainWindow.DeferLoadApplication(launchPathArg, startFullscreenArg); MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
} }
} }
@@ -215,6 +177,19 @@ namespace Ryujinx.Ava
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}"); Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
} }
} }
// Check if graphics backend was overridden
if (CommandLineState.OverrideGraphicsBackend != null)
{
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
{
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
}
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
{
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
}
}
} }
private static void PrintSystemInfo() private static void PrintSystemInfo()

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -8,11 +8,13 @@
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants> <DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
<RootNamespace>Ryujinx.Ava</RootNamespace> <RootNamespace>Ryujinx.Ava</RootNamespace>
<ApplicationIcon>Ryujinx.ico</ApplicationIcon> <ApplicationIcon>Ryujinx.ico</ApplicationIcon>
<TieredPGO>true</TieredPGO>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''"> <PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed> <PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -34,7 +36,7 @@
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" /> <PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
<PackageReference Include="SPB" Version="0.0.4-build24" /> <PackageReference Include="SPB" Version="0.0.4-build27" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup> </ItemGroup>

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
@@ -27,7 +28,10 @@ namespace Ryujinx.Ava.Ui.Controls
string secondaryButton, string secondaryButton,
string closeButton, string closeButton,
int iconSymbol, int iconSymbol,
UserResult primaryButtonResult = UserResult.Ok) UserResult primaryButtonResult = UserResult.Ok,
ManualResetEvent deferResetEvent = null,
Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{ {
UserResult result = UserResult.None; UserResult result = UserResult.None;
@@ -110,12 +114,19 @@ namespace Ryujinx.Ava.Ui.Controls
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{ {
result = UserResult.No; result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction;
}); });
contentDialog.CloseButtonCommand = MiniCommand.Create(() => contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{ {
result = UserResult.Cancel; result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction;
}); });
if (deferResetEvent != null)
{
contentDialog.PrimaryButtonClick += deferCloseAction;
}
await contentDialog.ShowAsync(ContentDialogPlacement.Popup); await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
overlay?.Close(); overlay?.Close();
@@ -143,35 +154,20 @@ namespace Ryujinx.Ava.Ui.Controls
Func<Window, Task> doWhileDeferred = null) Func<Window, Task> doWhileDeferred = null)
{ {
bool startedDeferring = false; bool startedDeferring = false;
UserResult result = UserResult.None; UserResult result = UserResult.None;
ContentDialog contentDialog = new ContentDialog return await ShowContentDialog(
{ title,
Title = title, primaryText,
PrimaryButtonText = primaryButton, secondaryText,
SecondaryButtonText = secondaryButton, primaryButton,
CloseButtonText = closeButton, secondaryButton,
Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol), closeButton,
PrimaryButtonCommand = MiniCommand.Create(() => iconSymbol,
{ primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok,
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok; deferResetEvent,
}), doWhileDeferred,
}; DeferClose);
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.No;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
contentDialog.PrimaryButtonClick -= DeferClose;
result = UserResult.Cancel;
});
contentDialog.PrimaryButtonClick += DeferClose;
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
return result;
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args) async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{ {
@@ -180,7 +176,7 @@ namespace Ryujinx.Ava.Ui.Controls
return; return;
} }
contentDialog.PrimaryButtonClick -= DeferClose; sender.PrimaryButtonClick -= DeferClose;
startedDeferring = true; startedDeferring = true;
@@ -188,7 +184,7 @@ namespace Ryujinx.Ava.Ui.Controls
result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok; result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
contentDialog.PrimaryButtonClick -= DeferClose; sender.PrimaryButtonClick -= DeferClose;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() => Task.Run(() =>

View File

@@ -21,14 +21,10 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.System;
using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone; using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.ViewModels namespace Ryujinx.Ava.Ui.ViewModels
@@ -138,6 +134,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public bool ExpandDramSize { get; set; } public bool ExpandDramSize { get; set; }
public bool EnableShaderCache { get; set; } public bool EnableShaderCache { get; set; }
public bool EnableTextureRecompression { get; set; } public bool EnableTextureRecompression { get; set; }
public bool EnableMacroHLE { get; set; }
public bool EnableFileLog { get; set; } public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; } public bool EnableStub { get; set; }
public bool EnableInfo { get; set; } public bool EnableInfo { get; set; }
@@ -339,6 +336,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
ExpandDramSize = config.System.ExpandRam; ExpandDramSize = config.System.ExpandRam;
EnableShaderCache = config.Graphics.EnableShaderCache; EnableShaderCache = config.Graphics.EnableShaderCache;
EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
EnableMacroHLE = config.Graphics.EnableMacroHLE;
EnableFileLog = config.Logger.EnableFileLog; EnableFileLog = config.Logger.EnableFileLog;
EnableStub = config.Logger.EnableStub; EnableStub = config.Logger.EnableStub;
EnableInfo = config.Logger.EnableInfo; EnableInfo = config.Logger.EnableInfo;
@@ -422,6 +420,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
config.Graphics.EnableVsync.Value = EnableVsync; config.Graphics.EnableVsync.Value = EnableVsync;
config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableShaderCache.Value = EnableShaderCache;
config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
config.System.EnablePtc.Value = EnablePptc; config.System.EnablePtc.Value = EnablePptc;
config.System.EnableInternetAccess.Value = EnableInternetAccess; config.System.EnableInternetAccess.Value = EnableInternetAccess;

View File

@@ -23,6 +23,7 @@ using Ryujinx.Modules;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
@@ -247,7 +248,7 @@ namespace Ryujinx.Ava.Ui.Windows
{ {
RendererControl.CreateVulkan(); RendererControl.CreateVulkan();
} }
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
if (!AppHost.LoadGuestApplication().Result) if (!AppHost.LoadGuestApplication().Result)
@@ -432,7 +433,7 @@ namespace Ryujinx.Ava.Ui.Windows
// Consider removing this at some point in the future when we don't need to worry about old saves. // Consider removing this at some point in the future when we don't need to worry about old saves.
VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient); VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient);
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, Program.CommandLineProfile); AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile);
VirtualFileSystem.ReloadKeySet(); VirtualFileSystem.ReloadKeySet();
@@ -525,6 +526,7 @@ namespace Ryujinx.Ava.Ui.Windows
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
} }
public void LoadHotKeys() public void LoadHotKeys()

View File

@@ -568,6 +568,10 @@
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}"> ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" /> <TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableMacroHLE}"
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
</CheckBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"

View File

@@ -53,5 +53,11 @@ namespace Ryujinx.Common
writer.Write(data); writer.Write(data);
} }
public static void Write(this BinaryWriter writer, UInt128 value)
{
writer.Write((ulong)value);
writer.Write((ulong)(value >> 64));
}
} }
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -7,19 +7,19 @@ namespace Ryujinx.Common.Utilities
public static class SpanHelpers public static class SpanHelpers
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> CreateSpan<T>(ref T reference, int length) public static Span<T> CreateSpan<T>(scoped ref T reference, int length)
{ {
return MemoryMarshal.CreateSpan(ref reference, length); return MemoryMarshal.CreateSpan(ref reference, length);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged public static Span<T> AsSpan<T>(scoped ref T reference) where T : unmanaged
{ {
return CreateSpan(ref reference, 1); return CreateSpan(ref reference, 1);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference) public static Span<TSpan> AsSpan<TStruct, TSpan>(scoped ref TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged where TStruct : unmanaged where TSpan : unmanaged
{ {
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference), return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
@@ -27,25 +27,25 @@ namespace Ryujinx.Common.Utilities
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged public static Span<byte> AsByteSpan<T>(scoped ref T reference) where T : unmanaged
{ {
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>()); return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) public static ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref T reference, int length)
{ {
return MemoryMarshal.CreateReadOnlySpan(ref reference, length); return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged public static ReadOnlySpan<T> AsReadOnlySpan<T>(scoped ref T reference) where T : unmanaged
{ {
return CreateReadOnlySpan(ref reference, 1); return CreateReadOnlySpan(ref reference, 1);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(ref TStruct reference) public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(scoped ref TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged where TStruct : unmanaged where TSpan : unmanaged
{ {
return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference), return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
@@ -53,7 +53,7 @@ namespace Ryujinx.Common.Utilities
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(ref T reference) where T : unmanaged public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(scoped ref T reference) where T : unmanaged
{ {
return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>()); return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
} }

View File

@@ -0,0 +1,17 @@
using System;
namespace Ryujinx.Common.Utilities
{
public static class UInt128Utils
{
public static UInt128 FromHex(string hex)
{
return new UInt128((ulong)Convert.ToInt64(hex.Substring(0, 16), 16), (ulong)Convert.ToInt64(hex.Substring(16), 16));
}
public static UInt128 CreateRandom()
{
return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64());
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -34,13 +34,14 @@ namespace Ryujinx.Graphics.GAL
int firstIndex, int firstIndex,
int firstVertex, int firstVertex,
int firstInstance); int firstInstance);
void DrawIndexedIndirect(BufferRange indirectBuffer);
void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
void DrawIndirect(BufferRange indirectBuffer);
void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion); void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion);
void EndTransformFeedback(); void EndTransformFeedback();
void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride);
void SetAlphaTest(bool enable, float reference, CompareOp op); void SetAlphaTest(bool enable, float reference, CompareOp op);
void SetBlendState(int index, BlendDescriptor blend); void SetBlendState(int index, BlendDescriptor blend);

View File

@@ -141,16 +141,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading
DrawCommand.Run(ref GetCommand<DrawCommand>(memory), threaded, renderer); DrawCommand.Run(ref GetCommand<DrawCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndexed] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.DrawIndexed] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndexedCommand.Run(ref GetCommand<DrawIndexedCommand>(memory), threaded, renderer); DrawIndexedCommand.Run(ref GetCommand<DrawIndexedCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndexedIndirect] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndexedIndirectCommand.Run(ref GetCommand<DrawIndexedIndirectCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndexedIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndexedIndirectCountCommand.Run(ref GetCommand<DrawIndexedIndirectCountCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndirect] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndirectCommand.Run(ref GetCommand<DrawIndirectCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndirectCountCommand.Run(ref GetCommand<DrawIndirectCountCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.DrawTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.DrawTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawTextureCommand.Run(ref GetCommand<DrawTextureCommand>(memory), threaded, renderer); DrawTextureCommand.Run(ref GetCommand<DrawTextureCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.EndHostConditionalRendering] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.EndHostConditionalRendering] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
EndHostConditionalRenderingCommand.Run(renderer); EndHostConditionalRenderingCommand.Run(renderer);
_lookup[(int)CommandType.EndTransformFeedback] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.EndTransformFeedback] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
EndTransformFeedbackCommand.Run(ref GetCommand<EndTransformFeedbackCommand>(memory), threaded, renderer); EndTransformFeedbackCommand.Run(ref GetCommand<EndTransformFeedbackCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.MultiDrawIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
MultiDrawIndirectCountCommand.Run(ref GetCommand<MultiDrawIndirectCountCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.MultiDrawIndexedIndirectCount] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
MultiDrawIndexedIndirectCountCommand.Run(ref GetCommand<MultiDrawIndexedIndirectCountCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetAlphaTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.SetAlphaTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
SetAlphaTestCommand.Run(ref GetCommand<SetAlphaTestCommand>(memory), threaded, renderer); SetAlphaTestCommand.Run(ref GetCommand<SetAlphaTestCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.SetBlendState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) => _lookup[(int)CommandType.SetBlendState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>

View File

@@ -52,11 +52,13 @@
DispatchCompute, DispatchCompute,
Draw, Draw,
DrawIndexed, DrawIndexed,
DrawIndexedIndirect,
DrawIndexedIndirectCount,
DrawIndirect,
DrawIndirectCount,
DrawTexture, DrawTexture,
EndHostConditionalRendering, EndHostConditionalRendering,
EndTransformFeedback, EndTransformFeedback,
MultiDrawIndirectCount,
MultiDrawIndexedIndirectCount,
SetAlphaTest, SetAlphaTest,
SetBlendState, SetBlendState,
SetDepthBias, SetDepthBias,

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct DrawIndexedIndirectCommand : IGALCommand
{
public CommandType CommandType => CommandType.DrawIndexedIndirect;
private BufferRange _indirectBuffer;
public void Set(BufferRange indirectBuffer)
{
_indirectBuffer = indirectBuffer;
}
public static void Run(ref DrawIndexedIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.DrawIndexedIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
}
}
}

View File

@@ -1,8 +1,8 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{ {
struct MultiDrawIndexedIndirectCountCommand : IGALCommand struct DrawIndexedIndirectCountCommand : IGALCommand
{ {
public CommandType CommandType => CommandType.MultiDrawIndexedIndirectCount; public CommandType CommandType => CommandType.DrawIndexedIndirectCount;
private BufferRange _indirectBuffer; private BufferRange _indirectBuffer;
private BufferRange _parameterBuffer; private BufferRange _parameterBuffer;
private int _maxDrawCount; private int _maxDrawCount;
@@ -16,9 +16,9 @@
_stride = stride; _stride = stride;
} }
public static void Run(ref MultiDrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref DrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.MultiDrawIndexedIndirectCount( renderer.Pipeline.DrawIndexedIndirectCount(
threaded.Buffers.MapBufferRange(command._indirectBuffer), threaded.Buffers.MapBufferRange(command._indirectBuffer),
threaded.Buffers.MapBufferRange(command._parameterBuffer), threaded.Buffers.MapBufferRange(command._parameterBuffer),
command._maxDrawCount, command._maxDrawCount,

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct DrawIndirectCommand : IGALCommand
{
public CommandType CommandType => CommandType.DrawIndirect;
private BufferRange _indirectBuffer;
public void Set(BufferRange indirectBuffer)
{
_indirectBuffer = indirectBuffer;
}
public static void Run(ref DrawIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.DrawIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
}
}
}

View File

@@ -1,8 +1,8 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{ {
struct MultiDrawIndirectCountCommand : IGALCommand struct DrawIndirectCountCommand : IGALCommand
{ {
public CommandType CommandType => CommandType.MultiDrawIndirectCount; public CommandType CommandType => CommandType.DrawIndirectCount;
private BufferRange _indirectBuffer; private BufferRange _indirectBuffer;
private BufferRange _parameterBuffer; private BufferRange _parameterBuffer;
private int _maxDrawCount; private int _maxDrawCount;
@@ -16,9 +16,9 @@
_stride = stride; _stride = stride;
} }
public static void Run(ref MultiDrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref DrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.MultiDrawIndirectCount( renderer.Pipeline.DrawIndirectCount(
threaded.Buffers.MapBufferRange(command._indirectBuffer), threaded.Buffers.MapBufferRange(command._indirectBuffer),
threaded.Buffers.MapBufferRange(command._parameterBuffer), threaded.Buffers.MapBufferRange(command._parameterBuffer),
command._maxDrawCount, command._maxDrawCount,

View File

@@ -83,6 +83,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void DrawIndexedIndirect(BufferRange indirectBuffer)
{
_renderer.New<DrawIndexedIndirectCommand>().Set(indirectBuffer);
_renderer.QueueCommand();
}
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
_renderer.New<DrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
_renderer.QueueCommand();
}
public void DrawIndirect(BufferRange indirectBuffer)
{
_renderer.New<DrawIndirectCommand>().Set(indirectBuffer);
_renderer.QueueCommand();
}
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
_renderer.New<DrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
_renderer.QueueCommand();
}
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{ {
_renderer.New<DrawTextureCommand>().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion); _renderer.New<DrawTextureCommand>().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion);
@@ -101,18 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
_renderer.New<MultiDrawIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
_renderer.QueueCommand();
}
public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
_renderer.New<MultiDrawIndexedIndirectCountCommand>().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
_renderer.QueueCommand();
}
public void SetAlphaTest(bool enable, float reference, CompareOp op) public void SetAlphaTest(bool enable, float reference, CompareOp op)
{ {
_renderer.New<SetAlphaTestCommand>().Set(enable, reference, op); _renderer.New<SetAlphaTestCommand>().Set(enable, reference, op);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

View File

@@ -94,6 +94,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{ {
var memoryManager = _channel.MemoryManager; var memoryManager = _channel.MemoryManager;
// Since we're going to change the state, make sure any pending instanced draws are done.
_3dEngine.PerformDeferredDraws();
// Make sure all pending uniform buffer data is written to memory.
_3dEngine.FlushUboDirty(); _3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA; uint qmdAddress = _state.State.SendPcasA;

View File

@@ -62,10 +62,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
} }
if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount) // We don't consume the parameter buffer value, so we don't need to flush it.
// Doing so improves performance if the value was written by a GPU shader.
if (_hleFunction == MacroHLEFunctionName.DrawElementsIndirect)
{
context.GPFifo.SetFlushSkips(1);
}
else if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
{ {
// We don't consume the parameter buffer value, so we don't need to flush it.
// Doing so improves performance if the value was written by a GPU shader.
context.GPFifo.SetFlushSkips(2); context.GPFifo.SetFlushSkips(2);
} }
} }

View File

@@ -16,6 +16,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private const int ColorStructSize = 0x40; private const int ColorStructSize = 0x40;
private const int ZetaLayerCountOffset = 0x1230; private const int ZetaLayerCountOffset = 0x1230;
private const int IndirectDataEntrySize = 0x10;
private const int IndirectIndexedDataEntrySize = 0x14;
private readonly GPFifoProcessor _processor; private readonly GPFifoProcessor _processor;
private readonly MacroHLEFunctionName _functionName; private readonly MacroHLEFunctionName _functionName;
@@ -27,9 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <summary> /// <summary>
/// Creates a new instance of the HLE macro handler. /// Creates a new instance of the HLE macro handler.
/// </summary> /// </summary>
/// <param name="context">GPU context the macro is being executed on</param> /// <param name="processor">GPU GP FIFO command processor</param>
/// <param name="memoryManager">GPU memory manager</param>
/// <param name="engine">3D engine where this macro is being called</param>
/// <param name="functionName">Name of the HLE macro function to be called</param> /// <param name="functionName">Name of the HLE macro function to be called</param>
public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName) public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
{ {
@@ -55,12 +56,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
case MacroHLEFunctionName.ClearDepthStencil: case MacroHLEFunctionName.ClearDepthStencil:
ClearDepthStencil(state, arg0); ClearDepthStencil(state, arg0);
break; break;
case MacroHLEFunctionName.DrawArraysInstanced:
DrawArraysInstanced(state, arg0);
break;
case MacroHLEFunctionName.DrawElementsInstanced:
DrawElementsInstanced(state, arg0);
break;
case MacroHLEFunctionName.DrawElementsIndirect:
DrawElementsIndirect(state, arg0);
break;
case MacroHLEFunctionName.MultiDrawElementsIndirectCount: case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
MultiDrawElementsIndirectCount(state, arg0); MultiDrawElementsIndirectCount(state, arg0);
break; break;
default: default:
throw new NotImplementedException(_functionName.ToString()); throw new NotImplementedException(_functionName.ToString());
} }
// It should be empty at this point, but clear it just to be safe.
Fifo.Clear();
} }
/// <summary> /// <summary>
@@ -89,7 +102,118 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
/// <summary> /// <summary>
/// Performs a indirect multi-draw, with parameters from a GPU buffer. /// Performs a draw.
/// </summary>
/// <param name="state">GPU state at the time of the call</param>
/// <param name="arg0">First argument of the call</param>
private void DrawArraysInstanced(IDeviceState state, int arg0)
{
var topology = (PrimitiveTopology)arg0;
var count = FetchParam();
var instanceCount = FetchParam();
var firstVertex = FetchParam();
var firstInstance = FetchParam();
if (ShouldSkipDraw(state, instanceCount.Word))
{
return;
}
_processor.ThreedClass.Draw(
topology,
count.Word,
instanceCount.Word,
0,
firstVertex.Word,
firstInstance.Word,
indexed: false);
}
/// <summary>
/// Performs a indexed draw.
/// </summary>
/// <param name="state">GPU state at the time of the call</param>
/// <param name="arg0">First argument of the call</param>
private void DrawElementsInstanced(IDeviceState state, int arg0)
{
var topology = (PrimitiveTopology)arg0;
var count = FetchParam();
var instanceCount = FetchParam();
var firstIndex = FetchParam();
var firstVertex = FetchParam();
var firstInstance = FetchParam();
if (ShouldSkipDraw(state, instanceCount.Word))
{
return;
}
_processor.ThreedClass.Draw(
topology,
count.Word,
instanceCount.Word,
firstIndex.Word,
firstVertex.Word,
firstInstance.Word,
indexed: true);
}
/// <summary>
/// Performs a indirect indexed draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="state">GPU state at the time of the call</param>
/// <param name="arg0">First argument of the call</param>
private void DrawElementsIndirect(IDeviceState state, int arg0)
{
var topology = (PrimitiveTopology)arg0;
var count = FetchParam();
var instanceCount = FetchParam();
var firstIndex = FetchParam();
var firstVertex = FetchParam();
var firstInstance = FetchParam();
ulong indirectBufferGpuVa = count.GpuVa;
var bufferCache = _processor.MemoryManager.Physical.BufferCache;
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
if (useBuffer)
{
int indexCount = firstIndex.Word + count.Word;
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferAddress,
0,
1,
IndirectIndexedDataEntrySize,
indexCount,
Threed.IndirectDrawType.DrawIndexedIndirect);
}
else
{
if (ShouldSkipDraw(state, instanceCount.Word))
{
return;
}
_processor.ThreedClass.Draw(
topology,
count.Word,
instanceCount.Word,
firstIndex.Word,
firstVertex.Word,
firstInstance.Word,
indexed: true);
}
}
/// <summary>
/// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
/// </summary> /// </summary>
/// <param name="state">GPU state at the time of the call</param> /// <param name="state">GPU state at the time of the call</param>
/// <param name="arg0">First argument of the call</param> /// <param name="arg0">First argument of the call</param>
@@ -132,8 +256,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
return; return;
} }
int indirectBufferSize = maxDrawCount * stride;
ulong indirectBufferGpuVa = 0; ulong indirectBufferGpuVa = 0;
int indexCount = 0; int indexCount = 0;
@@ -142,8 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
var count = FetchParam(); var count = FetchParam();
var instanceCount = FetchParam(); var instanceCount = FetchParam();
var firstIndex = FetchParam(); var firstIndex = FetchParam();
var baseVertex = FetchParam(); var firstVertex = FetchParam();
var baseInstance = FetchParam(); var firstInstance = FetchParam();
if (i == 0) if (i == 0)
{ {
@@ -161,15 +283,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
} }
// It should be empty at this point, but clear it just to be safe.
Fifo.Clear();
var bufferCache = _processor.MemoryManager.Physical.BufferCache; var bufferCache = _processor.MemoryManager.Physical.BufferCache;
var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4); ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize);
_processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride); ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferAddress,
parameterBufferAddress,
maxDrawCount,
stride,
indexCount,
Threed.IndirectDrawType.DrawIndexedIndirectCount);
}
/// <summary>
/// Checks if the draw should be skipped, because the masked instance count is zero.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="instanceCount">Draw instance count</param>
/// <returns>True if the draw should be skipped, false otherwise</returns>
private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
{
return (Read(state, 0xd1b) & instanceCount) == 0;
} }
/// <summary> /// <summary>
@@ -189,14 +328,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
/// <summary> /// <summary>
/// Performs a GPU method call. /// Reads data from a GPU register.
/// </summary> /// </summary>
/// <param name="state">Current GPU state</param> /// <param name="state">Current GPU state</param>
/// <param name="methAddr">Address, in words, of the method</param> /// <param name="reg">Register offset to read</param>
/// <param name="value">Call argument</param> /// <returns>GPU register value</returns>
private static void Send(IDeviceState state, int methAddr, int value) private static int Read(IDeviceState state, int reg)
{ {
state.Write(methAddr * 4, value); return state.Read(reg * 4);
} }
} }
} }

View File

@@ -8,6 +8,9 @@
None, None,
ClearColor, ClearColor,
ClearDepthStencil, ClearDepthStencil,
DrawArraysInstanced,
DrawElementsInstanced,
DrawElementsIndirect,
MultiDrawElementsIndirectCount MultiDrawElementsIndirectCount
} }
} }

View File

@@ -44,17 +44,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
} }
private static readonly TableEntry[] Table = new TableEntry[] private static readonly TableEntry[] _table = new TableEntry[]
{ {
new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28), new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24), new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C) new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C)
}; };
/// <summary>
/// Checks if the host supports all features required by the HLE macro.
/// </summary>
/// <param name="caps">Host capabilities</param>
/// <param name="name">Name of the HLE macro to be checked</param>
/// <returns>True if the host supports the HLE macro, false otherwise</returns>
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name) private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
{ {
if (name == MacroHLEFunctionName.ClearColor || if (name == MacroHLEFunctionName.ClearColor ||
name == MacroHLEFunctionName.ClearDepthStencil) name == MacroHLEFunctionName.ClearDepthStencil ||
name == MacroHLEFunctionName.DrawArraysInstanced ||
name == MacroHLEFunctionName.DrawElementsInstanced ||
name == MacroHLEFunctionName.DrawElementsIndirect)
{ {
return true; return true;
} }
@@ -77,15 +89,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
{ {
var mc = MemoryMarshal.Cast<int, byte>(code); var mc = MemoryMarshal.Cast<int, byte>(code);
for (int i = 0; i < Table.Length; i++) for (int i = 0; i < _table.Length; i++)
{ {
ref var entry = ref Table[i]; ref var entry = ref _table[i];
var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length)); var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length));
if (hash == entry.Hash) if (hash == entry.Hash)
{ {
name = entry.Name; if (IsMacroHLESupported(caps, entry.Name))
return IsMacroHLESupported(caps, name); {
name = entry.Name;
return true;
}
break;
} }
} }

View File

@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
else else
{ {
evt.Flush(); evt.Flush();
return (memoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; return (memoryManager.Read<ulong>(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
} }
} }
@@ -108,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
evt?.Flush(); evt?.Flush();
evt2?.Flush(); evt2?.Flush();
ulong x = memoryManager.Read<ulong>(gpuVa); ulong x = memoryManager.Read<ulong>(gpuVa, true);
ulong y = memoryManager.Read<ulong>(gpuVa + 16); ulong y = memoryManager.Read<ulong>(gpuVa + 16, true);
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
} }

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
namespace Ryujinx.Graphics.Gpu.Engine.Threed namespace Ryujinx.Graphics.Gpu.Engine.Threed
@@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
class DrawManager class DrawManager
{ {
// Since we don't know the index buffer size for indirect draws,
// we must assume a minimum and maximum size and use that for buffer data update purposes.
private const int MinIndirectIndexCount = 0x10000;
private const int MaxIndirectIndexCount = 0x4000000;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DeviceStateWithShadow<ThreedClassState> _state;
@@ -28,6 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private int _instanceIndex; private int _instanceIndex;
private const int VertexBufferFirstMethodOffset = 0x35d;
private const int IndexBufferCountMethodOffset = 0x5f8; private const int IndexBufferCountMethodOffset = 0x5f8;
/// <summary> /// <summary>
@@ -237,6 +244,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instanceIndex = 0; _instanceIndex = 0;
} }
UpdateTopology(topology);
}
/// <summary>
/// Updates the current primitive topology if needed.
/// </summary>
/// <param name="topology">New primitive topology</param>
private void UpdateTopology(PrimitiveTopology topology)
{
if (_drawState.Topology != topology || !_topologySet) if (_drawState.Topology != topology || !_topologySet)
{ {
_context.Renderer.Pipeline.SetPrimitiveTopology(topology); _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
@@ -383,28 +399,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
/// <summary> /// <summary>
/// Performs a indirect multi-draw, with parameters from a GPU buffer. /// Performs a indexed or non-indexed draw.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param> /// <param name="engine">3D engine where this method is being called</param>
/// <param name="topology">Primitive topology</param> /// <param name="topology">Primitive topology</param>
/// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param> /// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
/// <param name="parameterBuffer">GPU buffer with the draw count</param> /// <param name="instanceCount">Instance count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param> /// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
/// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param> /// <param name="firstVertex">First vertex on the vertex buffer</param>
public void MultiDrawIndirectCount( /// <param name="firstInstance">First instance</param>
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
public void Draw(
ThreedClass engine, ThreedClass engine,
int indexCount,
PrimitiveTopology topology, PrimitiveTopology topology,
BufferRange indirectBuffer, int count,
BufferRange parameterBuffer, int instanceCount,
int maxDrawCount, int firstIndex,
int stride) int firstVertex,
int firstInstance,
bool indexed)
{ {
engine.Write(IndexBufferCountMethodOffset * 4, indexCount); UpdateTopology(topology);
_context.Renderer.Pipeline.SetPrimitiveTopology(topology);
_drawState.Topology = topology;
_topologySet = true;
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
_context, _context,
@@ -418,21 +433,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return; return;
} }
_drawState.FirstIndex = _state.State.IndexBufferState.First; if (indexed)
_drawState.IndexCount = indexCount;
engine.UpdateState();
if (_drawState.DrawIndexed)
{ {
_context.Renderer.Pipeline.MultiDrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); _drawState.FirstIndex = firstIndex;
_drawState.IndexCount = count;
_state.State.FirstVertex = (uint)firstVertex;
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
} }
else else
{ {
_context.Renderer.Pipeline.MultiDrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride); _state.State.VertexBufferDrawState.First = firstVertex;
_state.State.VertexBufferDrawState.Count = count;
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
}
_state.State.FirstInstance = (uint)firstInstance;
_drawState.DrawIndexed = indexed;
_drawState.HasConstantBufferDrawParameters = true;
engine.UpdateState();
if (indexed)
{
_context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
_state.State.FirstVertex = 0;
}
else
{
_context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
}
_state.State.FirstInstance = 0;
_drawState.DrawIndexed = false;
_drawState.HasConstantBufferDrawParameters = false;
if (renderEnable == ConditionalRenderEnabled.Host)
{
_context.Renderer.Pipeline.EndHostConditionalRendering();
}
}
/// <summary>
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
public void DrawIndirect(
ThreedClass engine,
PrimitiveTopology topology,
ulong indirectBufferAddress,
ulong parameterBufferAddress,
int maxDrawCount,
int stride,
int indexCount,
IndirectDrawType drawType)
{
UpdateTopology(topology);
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
_context,
_channel.MemoryManager,
_state.State.RenderEnableAddress,
_state.State.RenderEnableCondition);
if (renderEnable == ConditionalRenderEnabled.False)
{
_drawState.DrawIndexed = false;
return;
}
PhysicalMemory memory = _channel.MemoryManager.Physical;
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
if (indexed)
{
indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount);
_drawState.FirstIndex = 0;
_drawState.IndexCount = indexCount;
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
}
_drawState.DrawIndexed = indexed;
_drawState.DrawIndirect = true;
_drawState.HasConstantBufferDrawParameters = true;
engine.UpdateState();
if (hasCount)
{
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride);
var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4);
if (indexed)
{
_context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
}
else
{
_context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
}
}
else
{
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride);
if (indexed)
{
_context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer);
}
else
{
_context.Renderer.Pipeline.DrawIndirect(indirectBuffer);
}
} }
_drawState.DrawIndexed = false; _drawState.DrawIndexed = false;
_drawState.DrawIndirect = false;
_drawState.HasConstantBufferDrawParameters = false;
if (renderEnable == ConditionalRenderEnabled.Host) if (renderEnable == ConditionalRenderEnabled.Host)
{ {

View File

@@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
public bool DrawIndexed; public bool DrawIndexed;
/// <summary>
/// Indicates if the next draw will be a indirect draw.
/// </summary>
public bool DrawIndirect;
/// <summary> /// <summary>
/// Indicates if any of the currently used vertex shaders reads the instance ID. /// Indicates if any of the currently used vertex shaders reads the instance ID.
/// </summary> /// </summary>
@@ -32,6 +37,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
public bool IsAnyVbInstanced; public bool IsAnyVbInstanced;
/// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary>
public bool HasConstantBufferDrawParameters;
/// <summary> /// <summary>
/// Primitive topology for the next draw. /// Primitive topology for the next draw.
/// </summary> /// </summary>

View File

@@ -0,0 +1,38 @@
namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
/// <summary>
/// Indirect draw type, which can be indexed or non-indexed, with or without a draw count.
/// </summary>
enum IndirectDrawType
{
/// <summary>
/// Non-indexed draw without draw count.
/// </summary>
DrawIndirect = 0,
/// <summary>
/// Indexed draw without draw count.
/// </summary>
DrawIndexedIndirect = Indexed,
/// <summary>
/// Non-indexed draw with draw count.
/// </summary>
DrawIndirectCount = Count,
/// <summary>
/// Indexed draw with draw count.
/// </summary>
DrawIndexedIndirectCount = Indexed | Count,
/// <summary>
/// Indexed flag.
/// </summary>
Indexed = 1 << 0,
/// <summary>
/// Draw count flag.
/// </summary>
Count = 1 << 1
}
}

View File

@@ -34,10 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline; private ProgramPipelineState _pipeline;
private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer; private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
private bool _prevDrawIndexed; private bool _prevDrawIndexed;
private bool _prevDrawIndirect;
private IndexType _prevIndexType; private IndexType _prevIndexType;
private uint _prevFirstVertex; private uint _prevFirstVertex;
private bool _prevTfEnable; private bool _prevTfEnable;
@@ -210,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// of the shader for the new state. // of the shader for the new state.
if (_shaderSpecState != null) if (_shaderSpecState != null)
{ {
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false)) if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false))
{ {
ForceShaderUpdate(); ForceShaderUpdate();
} }
@@ -237,6 +239,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_prevDrawIndexed = _drawState.DrawIndexed; _prevDrawIndexed = _drawState.DrawIndexed;
} }
// Some draw parameters are used to restrict the vertex buffer size,
// but they can't be used on indirect draws because their values are unknown in this case.
// When switching between indirect and non-indirect draw, we need to
// make sure the vertex buffer sizes are still correct.
if (_drawState.DrawIndirect != _prevDrawIndirect)
{
_updateTracker.ForceDirty(VertexBufferStateIndex);
}
// In some cases, the index type is also used to guess the // In some cases, the index type is also used to guess the
// vertex buffer size, so we must update it if the type changed too. // vertex buffer size, so we must update it if the type changed too.
if (_drawState.DrawIndexed && if (_drawState.DrawIndexed &&
@@ -938,6 +949,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.IsAnyVbInstanced = false; _drawState.IsAnyVbInstanced = false;
bool drawIndexed = _drawState.DrawIndexed;
bool drawIndirect = _drawState.DrawIndirect;
for (int index = 0; index < Constants.TotalVertexBuffers; index++) for (int index = 0; index < Constants.TotalVertexBuffers; index++)
{ {
var vertexBuffer = _state.State.VertexBufferState[index]; var vertexBuffer = _state.State.VertexBufferState[index];
@@ -965,14 +979,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ulong vbSize = endAddress.Pack() - address + 1; ulong vbSize = endAddress.Pack() - address + 1;
ulong size; ulong size;
if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced) if (_drawState.IbStreamer.HasInlineIndexData || drawIndexed || stride == 0 || instanced)
{ {
// This size may be (much) larger than the real vertex buffer size. // This size may be (much) larger than the real vertex buffer size.
// Avoid calculating it this way, unless we don't have any other option. // Avoid calculating it this way, unless we don't have any other option.
size = vbSize; size = vbSize;
if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced) if (stride > 0 && indexTypeSmall && drawIndexed && !drawIndirect && !instanced)
{ {
// If the index type is a small integer type, then we might be still able // If the index type is a small integer type, then we might be still able
// to reduce the vertex buffer size based on the maximum possible index value. // to reduce the vertex buffer size based on the maximum possible index value.
@@ -1207,6 +1221,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
byte oldVsClipDistancesWritten = _vsClipDistancesWritten; byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
_drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false; _drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false;
_vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
_vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0; _vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0;
if (oldVsClipDistancesWritten != _vsClipDistancesWritten) if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
@@ -1222,6 +1237,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.SetProgram(gs.HostProgram); _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
} }
/// <summary>
/// Updates bindings consumed by the shader stage on the texture and buffer managers.
/// </summary>
/// <param name="stage">Shader stage to have the bindings updated</param>
/// <param name="info">Shader stage bindings info</param>
private void UpdateStageBindings(int stage, ShaderProgramInfo info) private void UpdateStageBindings(int stage, ShaderProgramInfo info)
{ {
_currentProgramInfo[stage] = info; _currentProgramInfo[stage] = info;
@@ -1340,7 +1360,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.AlphaTestEnable, _state.State.AlphaTestEnable,
_state.State.AlphaTestFunc, _state.State.AlphaTestFunc,
_state.State.AlphaTestRef, _state.State.AlphaTestRef,
ref attributeTypes); ref attributeTypes,
_drawState.HasConstantBufferDrawParameters);
} }
/// <summary> /// <summary>

View File

@@ -497,6 +497,50 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return 0; return 0;
} }
/// <summary>
/// Performs a indexed or non-indexed draw.
/// </summary>
/// <param name="topology">Primitive topology</param>
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
/// <param name="instanceCount">Instance count</param>
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
/// <param name="firstVertex">First vertex on the vertex buffer</param>
/// <param name="firstInstance">First instance</param>
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
public void Draw(
PrimitiveTopology topology,
int count,
int instanceCount,
int firstIndex,
int firstVertex,
int firstInstance,
bool indexed)
{
_drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
}
/// <summary>
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
public void DrawIndirect(
PrimitiveTopology topology,
ulong indirectBufferAddress,
ulong parameterBufferAddress,
int maxDrawCount,
int stride,
int indexCount,
IndirectDrawType drawType)
{
_drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType);
}
/// <summary> /// <summary>
/// Clears the current color and depth-stencil buffers. /// Clears the current color and depth-stencil buffers.
/// Which buffers should be cleared can also specified with the arguments. /// Which buffers should be cleared can also specified with the arguments.
@@ -507,25 +551,5 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
_drawManager.Clear(this, argument, layerCount); _drawManager.Clear(this, argument, layerCount);
} }
/// <summary>
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="indexCount">Index Buffer Count</param>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBuffer">GPU buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBuffer">GPU buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
/// <param name="stride">Distance in bytes between each element on the <paramref name="indirectBuffer"/> array</param>
public void MultiDrawIndirectCount(
int indexCount,
PrimitiveTopology topology,
BufferRange indirectBuffer,
BufferRange parameterBuffer,
int maxDrawCount,
int stride)
{
_drawManager.MultiDrawIndirectCount(this, indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
}
} }
} }

View File

@@ -708,11 +708,12 @@ namespace Ryujinx.Graphics.Gpu.Image
else else
{ {
bool dataMatches = _currentData != null && data.SequenceEqual(_currentData); bool dataMatches = _currentData != null && data.SequenceEqual(_currentData);
_currentData = data.ToArray();
if (dataMatches) if (dataMatches)
{ {
return; return;
} }
_currentData = data.ToArray();
} }
} }

View File

@@ -413,10 +413,10 @@ namespace Ryujinx.Graphics.Gpu.Image
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateCachedBuffer( private void UpdateCachedBuffer(
int stageIndex, int stageIndex,
ref int cachedTextureBufferIndex, scoped ref int cachedTextureBufferIndex,
ref int cachedSamplerBufferIndex, scoped ref int cachedSamplerBufferIndex,
ref ReadOnlySpan<int> cachedTextureBuffer, scoped ref ReadOnlySpan<int> cachedTextureBuffer,
ref ReadOnlySpan<int> cachedSamplerBuffer, scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
int textureBufferIndex, int textureBufferIndex,
int samplerBufferIndex) int samplerBufferIndex)
{ {

View File

@@ -710,7 +710,12 @@ namespace Ryujinx.Graphics.Gpu.Image
break; break;
case Target.Texture2DMultisample: case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray: case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) // We don't support copy between multisample and non-multisample depth-stencil textures
// because there's no way to emulate that since most GPUs don't support writing a
// custom stencil value into the texture, among several other API limitations.
if ((rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) &&
!rhs.FormatInfo.Format.IsDepthOrStencil())
{ {
return TextureViewCompatibility.CopyOnly; return TextureViewCompatibility.CopyOnly;
} }

View File

@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private Buffer[] _bufferOverlaps; private Buffer[] _bufferOverlaps;
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache; private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
public event Action NotifyBuffersModified; public event Action NotifyBuffersModified;
@@ -45,6 +46,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity]; _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
_dirtyCache = new Dictionary<ulong, BufferCacheEntry>(); _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
// There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty.
_modifiedCache = new Dictionary<ulong, BufferCacheEntry>();
} }
/// <summary> /// <summary>
@@ -145,6 +149,30 @@ namespace Ryujinx.Graphics.Gpu.Memory
result.Buffer.ForceDirty(result.Address, size); result.Buffer.ForceDirty(result.Address, size);
} }
/// <summary>
/// Checks if the given buffer range has been GPU modifed.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <returns>True if modified, false otherwise</returns>
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
{
if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
result.EndGpuAddress < gpuVa + size ||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
{
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
_modifiedCache[gpuVa] = result;
}
outAddr = result.Address;
return result.Buffer.IsModified(result.Address, size);
}
/// <summary> /// <summary>
/// Creates a new buffer for the specified range, if needed. /// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists, /// If a buffer where this range can be fully contained already exists,
@@ -326,18 +354,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
buffer.SignalModified(address, size); buffer.SignalModified(address, size);
} }
/// <summary>
/// Gets a buffer sub-range for a given GPU memory range.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <returns>The buffer sub-range for the given range</returns>
public BufferRange GetGpuBufferRange(MemoryManager memoryManager, ulong gpuVa, ulong size)
{
return GetBufferRange(TranslateAndCreateBuffer(memoryManager, gpuVa, size), size);
}
/// <summary> /// <summary>
/// Gets a buffer sub-range starting at a given memory address. /// Gets a buffer sub-range starting at a given memory address.
/// </summary> /// </summary>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -129,6 +129,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.ConstantBufferUse[_stageIndex]; return _oldSpecState.ConstantBufferUse[_stageIndex];
} }
/// <inheritdoc/>
public bool QueryHasConstantBufferDrawParameters()
{
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3781; private const uint CodeGenVersion = 3747;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";
@@ -159,6 +159,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// Bit mask of the render target components written by the fragment stage. /// Bit mask of the render target components written by the fragment stage.
/// </summary> /// </summary>
public int FragmentOutputMap; public int FragmentOutputMap;
/// <summary>
/// Indicates if the vertex shader accesses draw parameters.
/// </summary>
public bool UsesDrawParameters;
} }
private readonly DiskCacheGuestStorage _guestStorage; private readonly DiskCacheGuestStorage _guestStorage;
@@ -771,6 +776,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
images, images,
dataInfo.Stage, dataInfo.Stage,
dataInfo.UsesInstanceId, dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer, dataInfo.UsesRtLayer,
dataInfo.ClipDistancesWritten, dataInfo.ClipDistancesWritten,
dataInfo.FragmentOutputMap); dataInfo.FragmentOutputMap);
@@ -796,6 +802,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataInfo.ImagesCount = (ushort)info.Images.Count; dataInfo.ImagesCount = (ushort)info.Images.Count;
dataInfo.Stage = info.Stage; dataInfo.Stage = info.Stage;
dataInfo.UsesInstanceId = info.UsesInstanceId; dataInfo.UsesInstanceId = info.UsesInstanceId;
dataInfo.UsesDrawParameters = info.UsesDrawParameters;
dataInfo.UsesRtLayer = info.UsesRtLayer; dataInfo.UsesRtLayer = info.UsesRtLayer;
dataInfo.ClipDistancesWritten = info.ClipDistancesWritten; dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
dataInfo.FragmentOutputMap = info.FragmentOutputMap; dataInfo.FragmentOutputMap = info.FragmentOutputMap;

View File

@@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return useMask; return useMask;
} }
/// <inheritdoc/>
public bool QueryHasConstantBufferDrawParameters()
{
return _state.GraphicsState.HasConstantBufferDrawParameters;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -77,6 +77,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public Array32<AttributeType> AttributeTypes; public Array32<AttributeType> AttributeTypes;
/// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary>
public readonly bool HasConstantBufferDrawParameters;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@@ -93,6 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param> /// <param name="alphaTestCompare">When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded</param>
/// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param> /// <param name="alphaTestReference">When alpha test is enabled, indicates the value to compare with the fragment output alpha</param>
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param> /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
public GpuChannelGraphicsState( public GpuChannelGraphicsState(
bool earlyZForce, bool earlyZForce,
PrimitiveTopology topology, PrimitiveTopology topology,
@@ -106,7 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool alphaTestEnable, bool alphaTestEnable,
CompareOp alphaTestCompare, CompareOp alphaTestCompare,
float alphaTestReference, float alphaTestReference,
ref Array32<AttributeType> attributeTypes) ref Array32<AttributeType> attributeTypes,
bool hasConstantBufferDrawParameters)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@@ -121,6 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
AlphaTestCompare = alphaTestCompare; AlphaTestCompare = alphaTestCompare;
AlphaTestReference = alphaTestReference; AlphaTestReference = alphaTestReference;
AttributeTypes = attributeTypes; AttributeTypes = attributeTypes;
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
} }
} }
} }

View File

@@ -350,7 +350,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable
/// <param name="item">The item on the table, if found, otherwise unmodified</param> /// <param name="item">The item on the table, if found, otherwise unmodified</param>
/// <param name="data">The data on the table, if found, otherwise unmodified</param> /// <param name="data">The data on the table, if found, otherwise unmodified</param>
/// <returns>Table lookup result</returns> /// <returns>Table lookup result</returns>
public SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, int size, ref T item, ref byte[] data) public SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, int size, scoped ref T item, scoped ref byte[] data)
{ {
if (_count == 0) if (_count == 0)
{ {

View File

@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable
/// <param name="item">The item on the table, if found, otherwise unmodified</param> /// <param name="item">The item on the table, if found, otherwise unmodified</param>
/// <param name="data">The data on the table, if found, otherwise unmodified</param> /// <param name="data">The data on the table, if found, otherwise unmodified</param>
/// <returns>Table lookup result</returns> /// <returns>Table lookup result</returns>
public PartitionHashTable<T>.SearchResult TryFindItem(ref SmartDataAccessor dataAccessor, ref T item, ref byte[] data) public PartitionHashTable<T>.SearchResult TryFindItem(scoped ref SmartDataAccessor dataAccessor, scoped ref T item, scoped ref byte[] data)
{ {
return _table.TryFindItem(ref dataAccessor, Size, ref item, ref data); return _table.TryFindItem(ref dataAccessor, Size, ref item, ref data);
} }

View File

@@ -520,7 +520,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
} }
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true); bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false;
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true);
} }
/// <summary> /// <summary>

View File

@@ -35,7 +35,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
foreach (var entry in _entries) foreach (var entry in _entries)
{ {
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true)) bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false;
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true))
{ {
program = entry; program = entry;
return true; return true;

View File

@@ -481,9 +481,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">Graphics state</param> /// <param name="graphicsState">Graphics state</param>
/// <param name="usesDrawParameters">Indicates whether the vertex shader accesses draw parameters</param>
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
/// <returns>True if the state matches, false otherwise</returns> /// <returns>True if the state matches, false otherwise</returns>
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures) public bool MatchesGraphics(
GpuChannel channel,
GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
bool usesDrawParameters,
bool checkTextures)
{ {
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable) if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
{ {
@@ -520,6 +526,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
if (usesDrawParameters && graphicsState.HasConstantBufferDrawParameters != GraphicsState.HasConstantBufferDrawParameters)
{
return false;
}
return Matches(channel, poolState, checkTextures, isCompute: false); return Matches(channel, poolState, checkTextures, isCompute: false);
} }
@@ -552,11 +563,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static void UpdateCachedBuffer( private static void UpdateCachedBuffer(
GpuChannel channel, GpuChannel channel,
bool isCompute, bool isCompute,
ref int cachedTextureBufferIndex, scoped ref int cachedTextureBufferIndex,
ref int cachedSamplerBufferIndex, scoped ref int cachedSamplerBufferIndex,
ref ReadOnlySpan<int> cachedTextureBuffer, scoped ref ReadOnlySpan<int> cachedTextureBuffer,
ref ReadOnlySpan<int> cachedSamplerBuffer, scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
ref int cachedStageIndex, scoped ref int cachedStageIndex,
int textureBufferIndex, int textureBufferIndex,
int samplerBufferIndex, int samplerBufferIndex,
int stageIndex) int stageIndex)

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -7,7 +7,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
{ {
unsafe class FFmpegContext : IDisposable unsafe class FFmpegContext : IDisposable
{ {
private readonly FFCodec.AVCodec_decode _decodeFrame; private unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
private readonly AVCodec_decode _decodeFrame;
private static readonly FFmpegApi.av_log_set_callback_callback _logFunc; private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
private readonly AVCodec* _codec; private readonly AVCodec* _codec;
private AVPacket* _packet; private AVPacket* _packet;
@@ -53,17 +55,17 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
// libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union. // libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24)) if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
{ {
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodec*)_codec)->CodecCallback); _decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodec<AVCodec>*)_codec)->CodecCallback);
} }
// libavcodec 59.x changed AvCodec private API layout. // libavcodec 59.x changed AvCodec private API layout.
else if (avCodecMajorVersion == 59) else if (avCodecMajorVersion == 59)
{ {
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode); _decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec501>*)_codec)->Decode);
} }
// libavcodec 58.x and lower // libavcodec 58.x and lower
else else
{ {
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode); _decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
} }
} }

View File

@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass; public unsafe IntPtr PrivClass;
public IntPtr Profiles; public IntPtr Profiles;
public unsafe byte* WrapperName; public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649 #pragma warning restore CS0649
} }
} }

View File

@@ -2,7 +2,7 @@
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {
struct AVCodecLegacy struct AVCodec501
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
public unsafe byte* Name; public unsafe byte* Name;
@@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass; public unsafe IntPtr PrivClass;
public IntPtr Profiles; public IntPtr Profiles;
public unsafe byte* WrapperName; public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649 #pragma warning restore CS0649
} }
} }

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr AvClass; public unsafe IntPtr AvClass;
public int LogLevelOffset; public int LogLevelOffset;
public int CodecType; public int CodecType;
public unsafe AVCodecLegacy* Codec; public unsafe AVCodec* Codec;
public AVCodecID CodecId; public AVCodecID CodecId;
public uint CodecTag; public uint CodecTag;
public IntPtr PrivData; public IntPtr PrivData;

View File

@@ -2,12 +2,10 @@
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {
struct FFCodec struct FFCodec<T> where T: struct
{ {
public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
#pragma warning disable CS0649 #pragma warning disable CS0649
public AVCodec Base; public T Base;
public int CapsInternalOrCbType; public int CapsInternalOrCbType;
public int PrivDataSize; public int PrivDataSize;
public IntPtr UpdateThreadContext; public IntPtr UpdateThreadContext;

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -34,5 +34,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
GL.ActiveTexture(TextureUnit.Texture0 + unit); GL.ActiveTexture(TextureUnit.Texture0 + unit);
GL.BindTexture(target, Handle); GL.BindTexture(target, Handle);
} }
public static void ClearBinding(int unit)
{
GL.ActiveTexture(TextureUnit.Texture0 + unit);
GL.BindTextureUnit(unit, 0);
}
} }
} }

View File

@@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
if (!destinationView.Target.IsMultisample() && Target.IsMultisample()) if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
{ {
_renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1); _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer, 1);
} }
else if (destinationView.Target.IsMultisample() && !Target.IsMultisample()) else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
{ {

View File

@@ -586,6 +586,95 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void DrawIndexedIndirect(BufferRange indirectBuffer)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
_vertexArray.SetRangeOfIndexBuffer();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset);
_vertexArray.RestoreIndexBuffer();
PostDraw();
}
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
_vertexArray.SetRangeOfIndexBuffer();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
GL.MultiDrawElementsIndirectCount(
_primitiveType,
(All)_elementsType,
(IntPtr)indirectBuffer.Offset,
(IntPtr)parameterBuffer.Offset,
maxDrawCount,
stride);
_vertexArray.RestoreIndexBuffer();
PostDraw();
}
public void DrawIndirect(BufferRange indirectBuffer)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset);
PostDraw();
}
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
GL.MultiDrawArraysIndirectCount(
_primitiveType,
(IntPtr)indirectBuffer.Offset,
(IntPtr)parameterBuffer.Offset,
maxDrawCount,
stride);
PostDraw();
}
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{ {
if (texture is TextureView view && sampler is Sampler samp) if (texture is TextureView view && sampler is Sampler samp)
@@ -683,57 +772,6 @@ namespace Ryujinx.Graphics.OpenGL
_tfEnabled = false; _tfEnabled = false;
} }
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
GL.MultiDrawArraysIndirectCount(
_primitiveType,
(IntPtr)indirectBuffer.Offset,
(IntPtr)parameterBuffer.Offset,
maxDrawCount,
stride);
PostDraw();
}
public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!_program.IsLinked)
{
Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
return;
}
PreDrawVbUnbounded();
_vertexArray.SetRangeOfIndexBuffer();
GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
GL.MultiDrawElementsIndirectCount(
_primitiveType,
(All)_elementsType,
(IntPtr)indirectBuffer.Offset,
(IntPtr)parameterBuffer.Offset,
maxDrawCount,
stride);
_vertexArray.RestoreIndexBuffer();
PostDraw();
}
public void SetAlphaTest(bool enable, float reference, CompareOp op) public void SetAlphaTest(bool enable, float reference, CompareOp op)
{ {
if (!enable) if (!enable)
@@ -919,6 +957,7 @@ namespace Ryujinx.Graphics.OpenGL
if (texture == null) if (texture == null)
{ {
GL.BindImageTexture(binding, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
return; return;
} }
@@ -1275,6 +1314,10 @@ namespace Ryujinx.Graphics.OpenGL
((TextureBase)texture).Bind(binding); ((TextureBase)texture).Bind(binding);
} }
} }
else
{
TextureBase.ClearBinding(binding);
}
Sampler glSampler = (Sampler)sampler; Sampler glSampler = (Sampler)sampler;

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -10,12 +10,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public StructuredFunction CurrentFunction { get; set; } public StructuredFunction CurrentFunction { get; set; }
public StructuredProgramInfo Info { get; }
public ShaderConfig Config { get; } public ShaderConfig Config { get; }
public OperandManager OperandManager { get; } public OperandManager OperandManager { get; }
private readonly StructuredProgramInfo _info;
private readonly StringBuilder _sb; private readonly StringBuilder _sb;
private int _level; private int _level;
@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) public CodeGenContext(StructuredProgramInfo info, ShaderConfig config)
{ {
_info = info; Info = info;
Config = config; Config = config;
OperandManager = new OperandManager(); OperandManager = new OperandManager();
@@ -72,19 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public StructuredFunction GetFunction(int id) public StructuredFunction GetFunction(int id)
{ {
return _info.Functions[id]; return Info.Functions[id];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
return _info.TransformFeedbackOutputs[index];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
{
int index = location / 4;
return _info.TransformFeedbackOutputs[index];
} }
private void UpdateIndentation() private void UpdateIndentation()

View File

@@ -46,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else else
{ {
if (context.Config.Stage == ShaderStage.Vertex)
{
context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable");
}
context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable"); context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable");
} }
@@ -210,7 +215,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
{ {
var tfOutput = context.GetTransformFeedbackOutput(AttributeConsts.PositionX); var tfOutput = context.Info.GetTransformFeedbackOutput(AttributeConsts.PositionX);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex");
@@ -604,19 +609,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline)
{ {
for (int c = 0; c < 4; c++) int attrOffset = AttributeConsts.UserAttributeBase + attr * 16;
int components = context.Config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
if (components > 1)
{ {
char swzMask = "xyzw"[c]; string type = components switch
{
2 => "vec2",
3 => "vec3",
4 => "vec4",
_ => "float"
};
string xfb = string.Empty; string xfb = string.Empty;
var tfOutput = context.GetTransformFeedbackOutput(attr, c); var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
} }
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};"); context.AppendLine($"layout (location = {attr}{xfb}) out {type} {name};");
}
else
{
for (int c = 0; c < 4; c++)
{
char swzMask = "xyzw"[c];
string xfb = string.Empty;
var tfOutput = context.Info.GetTransformFeedbackOutput(attrOffset + c * 4);
if (tfOutput.Valid)
{
xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}";
}
context.AppendLine($"layout (location = {attr}, component = {c}{xfb}) out float {name}_{swzMask};");
}
} }
} }
else else

View File

@@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute()) if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute())
{ {
bool perPatch = operand.Type == OperandType.AttributePerPatch; bool perPatch = operand.Type == OperandType.AttributePerPatch;
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch); dest = OperandManager.GetOutAttributeName(context, operand.Value, perPatch);
} }
else else
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
else if (node is AstOperand operand) else if (node is AstOperand operand)
{ {
return context.OperandManager.GetExpression(operand, context.Config); return context.OperandManager.GetExpression(context, operand);
} }
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");

View File

@@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (src2 is AstOperand operand && operand.Type == OperandType.Constant) if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{ {
int attrOffset = baseAttr.Value + (operand.Value << 2); int attrOffset = baseAttr.Value + (operand.Value << 2);
return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr); return OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: false, indexExpr);
} }
else else
{ {
@@ -332,7 +332,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if (src2 is AstOperand operand && operand.Type == OperandType.Constant) if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
{ {
int attrOffset = baseAttr.Value + (operand.Value << 2); int attrOffset = baseAttr.Value + (operand.Value << 2);
attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true); attrName = OperandManager.GetAttributeName(context, attrOffset, perPatch: false, isOutAttr: true);
} }
else else
{ {

View File

@@ -48,6 +48,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) },
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) },
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) },
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) },
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
// Special. // Special.
@@ -99,15 +104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return name; return name;
} }
public string GetExpression(AstOperand operand, ShaderConfig config) public string GetExpression(CodeGenContext context, AstOperand operand)
{ {
return operand.Type switch return operand.Type switch
{ {
OperandType.Argument => GetArgumentName(operand.Value), OperandType.Argument => GetArgumentName(operand.Value),
OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false), OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false),
OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true), OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true),
OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
OperandType.ConstantBuffer => GetConstantBufferName(operand, config), OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
OperandType.LocalVariable => _locals[operand], OperandType.LocalVariable => _locals[operand],
OperandType.Undefined => DefaultNames.UndefinedName, OperandType.Undefined => DefaultNames.UndefinedName,
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
@@ -149,13 +154,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement); return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
} }
public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch) public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch)
{ {
return GetAttributeName(value, config, perPatch, isOutAttr: true); return GetAttributeName(context, value, perPatch, isOutAttr: true);
} }
public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0") public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
{ {
ShaderConfig config = context.Config;
if ((value & AttributeConsts.LoadOutputMask) != 0) if ((value & AttributeConsts.LoadOutputMask) != 0)
{ {
isOutAttr = true; isOutAttr = true;
@@ -188,6 +195,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
{ {
int attrOffset = value;
value -= AttributeConsts.UserAttributeBase; value -= AttributeConsts.UserAttributeBase;
string prefix = isOutAttr string prefix = isOutAttr
@@ -211,14 +219,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
((config.LastInVertexPipeline && isOutAttr) || ((config.LastInVertexPipeline && isOutAttr) ||
(config.Stage == ShaderStage.Fragment && !isOutAttr))) (config.Stage == ShaderStage.Fragment && !isOutAttr)))
{ {
string name = $"{prefix}{(value >> 4)}_{swzMask}"; int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr)) if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
{ {
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]"; name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
} }
return name; return components > 1 ? name + '.' + swzMask : name;
} }
else else
{ {

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private const uint SpirvVersionRevision = 0; private const uint SpirvVersionRevision = 0;
private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision; private const uint SpirvVersionPacked = (SpirvVersionMajor << 16) | (SpirvVersionMinor << 8) | SpirvVersionRevision;
private readonly StructuredProgramInfo _info; public StructuredProgramInfo Info { get; }
public ShaderConfig Config { get; } public ShaderConfig Config { get; }
@@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GeneratorPool<Instruction> instPool, GeneratorPool<Instruction> instPool,
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool) GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
{ {
_info = info; Info = info;
Config = config; Config = config;
if (config.Stage == ShaderStage.Geometry) if (config.Stage == ShaderStage.Geometry)
@@ -262,6 +262,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Instruction ioVariable, elemIndex; Instruction ioVariable, elemIndex;
Instruction invocationId = null;
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd; bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (isUserAttr && if (isUserAttr &&
@@ -273,7 +280,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4); var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr)) bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
if (invocationId != null && isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
}
else if (isArray)
{ {
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
} }
@@ -300,6 +317,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
attrOffset = attr; attrOffset = attr;
type = elemType; type = elemType;
if (Config.LastInPipeline && isOutAttr)
{
int components = Info.GetTransformFeedbackOutputComponents(attr);
if (components > 1)
{
attrOffset &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
attrInfo = new AttributeInfo(attrOffset, (attr - attrOffset) / 4, components, type, false);
}
}
} }
ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset]; ioVariable = isOutAttr ? Outputs[attrOffset] : Inputs[attrOffset];
@@ -308,12 +337,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0) if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
{ {
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable; if (invocationId != null)
{
return isIndexed
? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index)
: AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId);
}
else
{
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
}
} }
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex()); elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
if (isIndexed) if (invocationId != null && isIndexed)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex);
}
else if (isIndexed)
{ {
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex); return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex);
} }
@@ -327,12 +373,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
Instruction invocationId = null;
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
}
elemType = AggregateType.FP32; elemType = AggregateType.FP32;
var ioVariable = isOutAttr ? OutputsArray : InputsArray; var ioVariable = isOutAttr ? OutputsArray : InputsArray;
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2)); var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3)); var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr)) bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
if (invocationId != null && isArray)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
}
else if (invocationId != null)
{
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
}
else if (isArray)
{ {
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex); return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
} }
@@ -485,18 +548,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return _functions[funcIndex]; return _functions[funcIndex];
} }
public TransformFeedbackOutput GetTransformFeedbackOutput(int location, int component)
{
int index = (AttributeConsts.UserAttributeBase / 4) + location * 4 + component;
return _info.TransformFeedbackOutputs[index];
}
public TransformFeedbackOutput GetTransformFeedbackOutput(int location)
{
int index = location / 4;
return _info.TransformFeedbackOutputs[index];
}
public Instruction GetType(AggregateType type, int length = 1) public Instruction GetType(AggregateType type, int length = 1)
{ {
if (type.HasFlag(AggregateType.Array)) if (type.HasFlag(AggregateType.Array))

View File

@@ -440,11 +440,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
PixelImap iq = PixelImap.Unused; PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment && if (context.Config.Stage == ShaderStage.Fragment)
attr >= AttributeConsts.UserAttributeBase &&
attr < AttributeConsts.UserAttributeEnd)
{ {
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
{
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
}
else
{
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
{
iq = PixelImap.Constant;
}
}
} }
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq); DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
@@ -473,6 +484,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4); var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes)); attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.TessellationControl)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(StorageClass.Output, attrType); var spvType = context.TypePointer(StorageClass.Output, attrType);
var spvVar = context.Variable(spvType, StorageClass.Output); var spvVar = context.Variable(spvType, StorageClass.Output);
@@ -511,7 +527,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
((isOutAttr && context.Config.LastInVertexPipeline) || ((isOutAttr && context.Config.LastInVertexPipeline) ||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment))) (!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
{ {
DeclareInputOrOutput(context, attr, (attr >> 2) & 3, isOutAttr, iq); DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
return; return;
} }
@@ -543,6 +559,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(storageClass, attrType); var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass); var spvVar = context.Variable(spvType, storageClass);
@@ -562,7 +583,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
{ {
var tfOutput = context.GetTransformFeedbackOutput(attrInfo.BaseValue); var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
@@ -585,24 +606,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
if (!isOutAttr) if (!isOutAttr &&
!perPatch &&
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{ {
if (!perPatch && context.Decorate(spvVar, Decoration.PassthroughNV);
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
switch (iq)
{
case PixelImap.Constant:
context.Decorate(spvVar, Decoration.Flat);
break;
case PixelImap.ScreenLinear:
context.Decorate(spvVar, Decoration.NoPerspective);
break;
}
} }
} }
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
@@ -611,22 +620,52 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
} }
if (!isOutAttr)
{
switch (iq)
{
case PixelImap.Constant:
context.Decorate(spvVar, Decoration.Flat);
break;
case PixelImap.ScreenLinear:
context.Decorate(spvVar, Decoration.NoPerspective);
break;
}
}
context.AddGlobalVariable(spvVar); context.AddGlobalVariable(spvVar);
dict.Add(attrInfo.BaseValue, spvVar); dict.Add(attrInfo.BaseValue, spvVar);
} }
private static void DeclareInputOrOutput(CodeGenContext context, int attr, int component, bool isOutAttr, PixelImap iq = PixelImap.Unused) private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
{ {
var dict = isOutAttr ? context.Outputs : context.Inputs; var dict = isOutAttr ? context.Outputs : context.Inputs;
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr); var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
bool hasComponent = true;
int component = (attr >> 2) & 3;
int components = 1;
var type = attrInfo.Type & AggregateType.ElementTypeMask;
if (context.Config.LastInPipeline && isOutAttr)
{
components = context.Info.GetTransformFeedbackOutputComponents(attr);
if (components > 1)
{
attr &= ~0xf;
type = AggregateType.Vector | AggregateType.FP32;
hasComponent = false;
}
}
if (dict.ContainsKey(attr)) if (dict.ContainsKey(attr))
{ {
return; return;
} }
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input; var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
var attrType = context.GetType(attrInfo.Type & AggregateType.ElementTypeMask); var attrType = context.GetType(type, components);
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr))) if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
{ {
@@ -634,6 +673,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize)); attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
} }
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(storageClass, attrType); var spvType = context.TypePointer(storageClass, attrType);
var spvVar = context.Variable(spvType, storageClass); var spvVar = context.Variable(spvType, storageClass);
@@ -641,11 +685,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
int location = (attr - AttributeConsts.UserAttributeBase) / 16; int location = (attr - AttributeConsts.UserAttributeBase) / 16;
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
if (hasComponent)
{
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
}
if (isOutAttr) if (isOutAttr)
{ {
var tfOutput = context.GetTransformFeedbackOutput(location, component); var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
if (tfOutput.Valid) if (tfOutput.Valid)
{ {
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer); context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
@@ -689,8 +737,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance, AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
AttributeConsts.PointCoordX => BuiltIn.PointCoord, AttributeConsts.PointCoordX => BuiltIn.PointCoord,
AttributeConsts.TessCoordX => BuiltIn.TessCoord, AttributeConsts.TessCoordX => BuiltIn.TessCoord,
AttributeConsts.InstanceId => BuiltIn.InstanceId, // FIXME: Invalid AttributeConsts.InstanceId => BuiltIn.InstanceId,
AttributeConsts.VertexId => BuiltIn.VertexId, // FIXME: Invalid AttributeConsts.VertexId => BuiltIn.VertexId,
AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
AttributeConsts.FrontFacing => BuiltIn.FrontFacing, AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth, AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation, AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,

View File

@@ -62,10 +62,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.TransformFeedback); context.AddCapability(Capability.TransformFeedback);
} }
if (config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) if (config.Stage == ShaderStage.Fragment)
{ {
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); if (context.Info.Inputs.Contains(AttributeConsts.Layer))
context.AddExtension("SPV_EXT_fragment_shader_interlock"); {
context.AddCapability(Capability.Geometry);
}
if (context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock())
{
context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
context.AddExtension("SPV_EXT_fragment_shader_interlock");
}
} }
else if (config.Stage == ShaderStage.Geometry) else if (config.Stage == ShaderStage.Geometry)
{ {
@@ -81,6 +89,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
context.AddCapability(Capability.Tessellation); context.AddCapability(Capability.Tessellation);
} }
else if (config.Stage == ShaderStage.Vertex)
{
context.AddCapability(Capability.DrawParameters);
}
context.AddExtension("SPV_KHR_shader_ballot"); context.AddExtension("SPV_KHR_shader_ballot");
context.AddExtension("SPV_KHR_subgroup_vote"); context.AddExtension("SPV_KHR_subgroup_vote");

View File

@@ -6,5 +6,9 @@ namespace Ryujinx.Graphics.Shader
public const int MaxAttributes = 16; public const int MaxAttributes = 16;
public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes)); public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes));
public const int NvnBaseVertexByteOffset = 0x640;
public const int NvnBaseInstanceByteOffset = 0x644;
public const int NvnDrawIndexByteOffset = 0x648;
} }
} }

View File

@@ -168,6 +168,15 @@ namespace Ryujinx.Graphics.Shader
return 0; return 0;
} }
/// <summary>
/// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0.
/// </summary>
/// <returns>True if the shader translator can assume that the constant buffer contains the base IDs, false otherwise</returns>
bool QueryHasConstantBufferDrawParameters()
{
return false;
}
/// <summary> /// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug. /// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary> /// </summary>

View File

@@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
offset |= AttributeConsts.LoadOutputMask; offset |= AttributeConsts.LoadOutputMask;
} }
Operand src = op.P ? AttributePerPatch(offset) : Attribute(offset); Operand src = op.P ? AttributePerPatch(offset) : CreateInputAttribute(context, offset);
context.Copy(Register(rd), src); context.Copy(Register(rd), src);
} }
@@ -312,5 +312,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
return attr; return attr;
} }
private static Operand CreateInputAttribute(EmitterContext context, int attr)
{
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
if (attr == AttributeConsts.InstanceId)
{
return context.ISubtract(Attribute(AttributeConsts.InstanceIndex), Attribute(AttributeConsts.BaseInstance));
}
else if (attr == AttributeConsts.VertexId)
{
return Attribute(AttributeConsts.VertexIndex);
}
}
return Attribute(attr);
}
} }
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool UsesInstanceId { get; } public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; } public bool UsesRtLayer { get; }
public byte ClipDistancesWritten { get; } public byte ClipDistancesWritten { get; }
public int FragmentOutputMap { get; } public int FragmentOutputMap { get; }
@@ -23,6 +24,7 @@ namespace Ryujinx.Graphics.Shader
TextureDescriptor[] images, TextureDescriptor[] images,
ShaderStage stage, ShaderStage stage,
bool usesInstanceId, bool usesInstanceId,
bool usesDrawParameters,
bool usesRtLayer, bool usesRtLayer,
byte clipDistancesWritten, byte clipDistancesWritten,
int fragmentOutputMap) int fragmentOutputMap)
@@ -34,6 +36,7 @@ namespace Ryujinx.Graphics.Shader
Stage = stage; Stage = stage;
UsesInstanceId = usesInstanceId; UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer; UsesRtLayer = usesRtLayer;
ClipDistancesWritten = clipDistancesWritten; ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap; FragmentOutputMap = fragmentOutputMap;

View File

@@ -71,12 +71,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); var locations = config.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex);
var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); var stride = config.GpuAccessor.QueryTransformFeedbackStride(tfbIndex);
for (int j = 0; j < locations.Length; j++) for (int i = 0; i < locations.Length; i++)
{ {
byte location = locations[j]; byte location = locations[i];
if (location < 0xc0) if (location < 0xc0)
{ {
context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, j * 4, stride); context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, i * 4, stride);
} }
} }
} }

View File

@@ -37,7 +37,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Config = config; Config = config;
if (config.GpPassthrough) if (config.Stage == ShaderStage.TessellationControl)
{
// Required to index outputs.
Info.Inputs.Add(AttributeConsts.InvocationId);
}
else if (config.GpPassthrough)
{ {
int passthroughAttributes = config.PassthroughAttributes; int passthroughAttributes = config.PassthroughAttributes;
while (passthroughAttributes != 0) while (passthroughAttributes != 0)

View File

@@ -42,5 +42,40 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0]; TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
} }
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
{
int index = attr / 4;
return TransformFeedbackOutputs[index];
}
public int GetTransformFeedbackOutputComponents(int attr)
{
int index = attr / 4;
int baseIndex = index & ~3;
int count = 1;
for (; count < 4; count++)
{
ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
int prevOffset = prev.Offset;
int currOffset = curr.Offset;
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
{
break;
}
}
if (baseIndex + count <= index)
{
return 1;
}
return count;
}
} }
} }

View File

@@ -95,5 +95,11 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int LtMask = 0x2000040; public const int LtMask = 0x2000040;
public const int ThreadKill = 0x2000044; public const int ThreadKill = 0x2000044;
public const int BaseInstance = 0x2000050;
public const int BaseVertex = 0x2000054;
public const int InstanceIndex = 0x2000058;
public const int VertexIndex = 0x200005c;
public const int DrawIndex = 0x2000060;
} }
} }

View File

@@ -27,6 +27,11 @@ namespace Ryujinx.Graphics.Shader.Translation
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) }, { AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector | AggregateType.FP32) },
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) }, { AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) }, { AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) }, { AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
// Special. // Special.

View File

@@ -17,10 +17,11 @@ namespace Ryujinx.Graphics.Shader.Translation
Bindless = 1 << 2, Bindless = 1 << 2,
InstanceId = 1 << 3, InstanceId = 1 << 3,
RtLayer = 1 << 4, DrawParameters = 1 << 4,
CbIndexing = 1 << 5, RtLayer = 1 << 5,
IaIndexing = 1 << 6, CbIndexing = 1 << 6,
OaIndexing = 1 << 7, IaIndexing = 1 << 7,
FixedFuncAttr = 1 << 8 OaIndexing = 1 << 8,
FixedFuncAttr = 1 << 9
} }
} }

View File

@@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
public static void RunPass(BasicBlock[] blocks, ShaderConfig config) public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
{ {
bool isVertexShader = config.Stage == ShaderStage.Vertex;
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{ {
BasicBlock block = blocks[blkIndex]; BasicBlock block = blocks[blkIndex];
@@ -23,6 +26,21 @@ namespace Ryujinx.Graphics.Shader.Translation
continue; continue;
} }
if (isVertexShader)
{
if (hasConstantBufferDrawParameters)
{
if (ReplaceConstantBufferWithDrawParameters(operation))
{
config.SetUsedFeature(FeatureFlags.DrawParameters);
}
}
else if (HasConstantBufferDrawParameters(operation))
{
config.SetUsedFeature(FeatureFlags.DrawParameters);
}
}
if (UsesGlobalMemory(operation.Inst)) if (UsesGlobalMemory(operation.Inst))
{ {
node = RewriteGlobalAccess(node, config); node = RewriteGlobalAccess(node, config);
@@ -528,5 +546,57 @@ namespace Ryujinx.Graphics.Shader.Translation
return node; return node;
} }
private static bool ReplaceConstantBufferWithDrawParameters(Operation operation)
{
bool modified = false;
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
{
Operand src = operation.GetSource(srcIndex);
if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
{
switch (src.GetCbufOffset())
{
case Constants.NvnBaseVertexByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex));
modified = true;
break;
case Constants.NvnBaseInstanceByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance));
modified = true;
break;
case Constants.NvnDrawIndexByteOffset / 4:
operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex));
modified = true;
break;
}
}
}
return modified;
}
private static bool HasConstantBufferDrawParameters(Operation operation)
{
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
{
Operand src = operation.GetSource(srcIndex);
if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
{
switch (src.GetCbufOffset())
{
case Constants.NvnBaseVertexByteOffset / 4:
case Constants.NvnBaseInstanceByteOffset / 4:
case Constants.NvnDrawIndexByteOffset / 4:
return true;
}
}
}
return false;
}
} }
} }

View File

@@ -17,6 +17,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool GpPassthrough { get; } public bool GpPassthrough { get; }
public bool LastInPipeline { get; private set; }
public bool LastInVertexPipeline { get; private set; } public bool LastInVertexPipeline { get; private set; }
public int ThreadsPerInputPrimitive { get; } public int ThreadsPerInputPrimitive { get; }
@@ -143,6 +144,7 @@ namespace Ryujinx.Graphics.Shader.Translation
OmapSampleMask = header.OmapSampleMask; OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth; OmapDepth = header.OmapDepth;
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
LastInPipeline = true;
LastInVertexPipeline = header.Stage < ShaderStage.Fragment; LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
} }
@@ -254,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributes |= mask; UsedInputAttributes |= mask;
_thisUsedInputAttributes |= mask; _thisUsedInputAttributes |= mask;
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component); ThisInputAttributesComponents |= UInt128.One << (index * 4 + component);
} }
public void SetInputUserAttributePerPatch(int index) public void SetInputUserAttributePerPatch(int index)
@@ -306,6 +308,8 @@ namespace Ryujinx.Graphics.Shader.Translation
config._perPatchAttributeLocations = locationsMap; config._perPatchAttributeLocations = locationsMap;
} }
LastInPipeline = false;
// We don't consider geometry shaders using the geometry shader passthrough feature // We don't consider geometry shaders using the geometry shader passthrough feature
// as being the last because when this feature is used, it can't actually modify any of the outputs, // as being the last because when this feature is used, it can't actually modify any of the outputs,
// so the stage that comes before it is the last one that can do modifications. // so the stage that comes before it is the last one that can do modifications.

View File

@@ -86,6 +86,7 @@ namespace Ryujinx.Graphics.Shader.Translation
config.GetImageDescriptors(), config.GetImageDescriptors(),
config.Stage, config.Stage,
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
config.ClipDistancesWritten, config.ClipDistancesWritten,
config.OmapTargets); config.OmapTargets);
@@ -190,10 +191,10 @@ namespace Ryujinx.Graphics.Shader.Translation
UInt128 usedAttributes = context.Config.NextInputAttributesComponents; UInt128 usedAttributes = context.Config.NextInputAttributesComponents;
while (usedAttributes != UInt128.Zero) while (usedAttributes != UInt128.Zero)
{ {
int index = usedAttributes.TrailingZeroCount(); int index = (int)UInt128.TrailingZeroCount(usedAttributes);
int vecIndex = index / 4; int vecIndex = index / 4;
usedAttributes &= ~UInt128.Pow2(index); usedAttributes &= ~(UInt128.One << index);
// We don't need to initialize passthrough attributes. // We don't need to initialize passthrough attributes.
if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0) if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0)

View File

@@ -1,112 +0,0 @@
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.Translation
{
struct UInt128 : IEquatable<UInt128>
{
public static UInt128 Zero => new UInt128() { _v0 = 0, _v1 = 0 };
private ulong _v0;
private ulong _v1;
public UInt128(ulong low, ulong high)
{
_v0 = low;
_v1 = high;
}
public int TrailingZeroCount()
{
int count = BitOperations.TrailingZeroCount(_v0);
if (count == 64)
{
count += BitOperations.TrailingZeroCount(_v1);
}
return count;
}
public static UInt128 Pow2(int x)
{
if (x >= 64)
{
return new UInt128(0, 1UL << (x - 64));
}
return new UInt128(1UL << x, 0);
}
public static UInt128 operator ~(UInt128 x)
{
return new UInt128(~x._v0, ~x._v1);
}
public static UInt128 operator &(UInt128 x, UInt128 y)
{
return new UInt128(x._v0 & y._v0, x._v1 & y._v1);
}
public static UInt128 operator |(UInt128 x, UInt128 y)
{
return new UInt128(x._v0 | y._v0, x._v1 | y._v1);
}
public static UInt128 operator <<(UInt128 x, int shift)
{
if (shift == 0)
{
return new UInt128(x._v0, x._v1);
}
else if (shift >= 64)
{
return new UInt128(0, x._v0 << (shift - 64));
}
ulong shiftOut = x._v0 >> (64 - shift);
return new UInt128(x._v0 << shift, (x._v1 << shift) | shiftOut);
}
public static UInt128 operator >>(UInt128 x, int shift)
{
if (shift == 0)
{
return new UInt128(x._v0, x._v1);
}
else if (shift >= 64)
{
return new UInt128(x._v1 >> (shift - 64), 0);
}
ulong shiftOut = x._v1 & ((1UL << shift) - 1);
return new UInt128((x._v0 >> shift) | (shiftOut << (64 - shift)), x._v1 >> shift);
}
public static bool operator ==(UInt128 x, UInt128 y)
{
return x.Equals(y);
}
public static bool operator !=(UInt128 x, UInt128 y)
{
return !x.Equals(y);
}
public override bool Equals(object obj)
{
return obj is UInt128 other && Equals(other);
}
public bool Equals(UInt128 other)
{
return _v0 == other._v0 && _v1 == other._v1;
}
public override int GetHashCode()
{
return HashCode.Combine(_v0, _v1);
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@@ -229,8 +229,8 @@ namespace Ryujinx.Graphics.Vic.Image
private static RentedBuffer ReadBuffer( private static RentedBuffer ReadBuffer(
ResourceManager rm, ResourceManager rm,
ref SlotConfig config, scoped ref SlotConfig config,
ref Array8<PlaneOffsets> offsets, scoped ref Array8<PlaneOffsets> offsets,
bool linear, bool linear,
int plane, int plane,
int width, int width,

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

Some files were not shown because too many files have changed in this diff Show More