Compare commits
No commits in common. "master" and "1.1.1389" have entirely different histories.
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -23,7 +23,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Log file
|
label: Log file
|
||||||
description: A log file will help our developers to better diagnose and fix the issue.
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
|
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@ -83,4 +83,4 @@ body:
|
|||||||
- Additional info about your environment:
|
- Additional info about your environment:
|
||||||
- Any other information relevant to your issue.
|
- Any other information relevant to your issue.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
40
.github/dependabot.yml
vendored
Normal file
40
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
labels:
|
||||||
|
- "infra"
|
||||||
|
reviewers:
|
||||||
|
- TSRBerry
|
||||||
|
commit-message:
|
||||||
|
prefix: "ci"
|
||||||
|
|
||||||
|
- package-ecosystem: nuget
|
||||||
|
directory: /
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
labels:
|
||||||
|
- "infra"
|
||||||
|
reviewers:
|
||||||
|
- TSRBerry
|
||||||
|
commit-message:
|
||||||
|
prefix: nuget
|
||||||
|
groups:
|
||||||
|
Avalonia:
|
||||||
|
patterns:
|
||||||
|
- "*Avalonia*"
|
||||||
|
Silk.NET:
|
||||||
|
patterns:
|
||||||
|
- "Silk.NET*"
|
||||||
|
OpenTK:
|
||||||
|
patterns:
|
||||||
|
- "OpenTK*"
|
||||||
|
SixLabors:
|
||||||
|
patterns:
|
||||||
|
- "SixLabors*"
|
||||||
|
NUnit:
|
||||||
|
patterns:
|
||||||
|
- "NUnit*"
|
29
README.md
29
README.md
@ -1,27 +1,24 @@
|
|||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<br>
|
<br>
|
||||||
<a href="https://ryujinx.org/"><img src="https://raw.githubusercontent.com/natesworksdev/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
|
<a href="https://ryujinx.org/"><img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
|
||||||
<br>
|
<br>
|
||||||
<b>Ryujinx Mirror</b>
|
<b>Ryujinx</b>
|
||||||
<br>
|
<br>
|
||||||
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
||||||
<br>
|
<br>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p>
|
<p align="center">
|
||||||
Mirror of the Ryujinx open-source Nintendo Switch emulator, created by gdkchan, written in C#. It has been removed from github recently and this is a replica of it's state before being deleted with only readme changes.<a href= "https://www.reddit.com/r/pcgaming/comments/1ftvp49/ryujinx_a_nintendo_switch_emulator_has_ceased/">Learn more</a>
|
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
||||||
|
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
|
|
||||||
It was written from scratch and development on the project began in September 2017.
|
It was written from scratch and development on the project began in September 2017.
|
||||||
|
Ryujinx is available on Github under the <a href="https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||||
Ryujinx is available on Github under the <a href="https://github.com/natesworksdev/Ryujinx/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/natesworksdev/Ryujinx/actions/workflows/release.yml">
|
<a href="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml">
|
||||||
<img src="https://github.com/natesworksdev/Ryujinx/actions/workflows/release.yml/badge.svg"
|
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
alt="">
|
alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://crwd.in/ryujinx">
|
<a href="https://crwd.in/ryujinx">
|
||||||
@ -34,7 +31,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<img src="https://raw.githubusercontent.com/natesworksdev/Ryujinx-Website/master/public/assets/images/shell.png">
|
<img src="https://raw.githubusercontent.com/Ryujinx/Ryujinx-Website/master/public/assets/images/shell.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
@ -42,7 +39,7 @@
|
|||||||
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
|
As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
|
||||||
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
|
over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
|
||||||
|
|
||||||
You can check out the compatibility list [here](https://github.com/natesworksdev/Ryujinx-Games-List/issues).
|
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;
|
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.
|
simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue.
|
||||||
@ -53,10 +50,10 @@ Use the search function to see if a game has been tested already!
|
|||||||
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
|
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
|
||||||
failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||||
|
|
||||||
See our [Setup & Configuration Guide](https://github.com/natesworksdev/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
||||||
|
|
||||||
For our Local Wireless (LDN) builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
For our Local Wireless (LDN) builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
||||||
](https://github.com/natesworksdev/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
||||||
|
|
||||||
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
||||||
|
|
||||||
@ -65,7 +62,7 @@ Avalonia UI comes with translations for various languages. See [Crowdin](https:/
|
|||||||
These builds are compiled automatically for each commit on the master branch.
|
These builds are compiled automatically for each commit on the master branch.
|
||||||
While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
|
While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
|
||||||
|
|
||||||
If you want to see details on updates to the emulator, you can visit our [Changelog](https://github.com/natesworksdev/Ryujinx/wiki/Changelog).
|
If you want to see details on updates to the emulator, you can visit our [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog).
|
||||||
|
|
||||||
The latest automatic build for Windows, macOS, and Linux can be found on the [Official Website](https://ryujinx.org/download).
|
The latest automatic build for Windows, macOS, and Linux can be found on the [Official Website](https://ryujinx.org/download).
|
||||||
|
|
||||||
@ -141,7 +138,7 @@ This folder is located in the user folder, which can be accessed by clicking `Op
|
|||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx).
|
If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx).
|
||||||
You may also review our [FAQ](https://github.com/natesworksdev/Ryujinx/wiki/Frequently-Asked-Questions).
|
You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions).
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
{
|
{
|
||||||
@ -23,20 +21,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CompressorParameter Parameter => _parameter;
|
public CompressorParameter Parameter => _parameter;
|
||||||
public Memory<CompressorState> State { get; }
|
public Memory<CompressorState> State { get; }
|
||||||
public Memory<EffectResultState> ResultState { get; }
|
|
||||||
public ushort[] OutputBufferIndices { get; }
|
public ushort[] OutputBufferIndices { get; }
|
||||||
public ushort[] InputBufferIndices { get; }
|
public ushort[] InputBufferIndices { get; }
|
||||||
public bool IsEffectEnabled { get; }
|
public bool IsEffectEnabled { get; }
|
||||||
|
|
||||||
private CompressorParameter _parameter;
|
private CompressorParameter _parameter;
|
||||||
|
|
||||||
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> resultState, bool isEnabled, int nodeId)
|
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
_parameter = parameter;
|
_parameter = parameter;
|
||||||
State = state;
|
State = state;
|
||||||
ResultState = resultState;
|
|
||||||
|
|
||||||
IsEffectEnabled = isEnabled;
|
IsEffectEnabled = isEnabled;
|
||||||
|
|
||||||
@ -75,16 +71,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
|
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
{
|
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
|
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
|
||||||
|
|
||||||
statistics.Reset(_parameter.ChannelCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
|
||||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
|
||||||
Span<float> channelInput = stackalloc float[_parameter.ChannelCount];
|
|
||||||
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
||||||
float unknown4 = state.Unknown4;
|
float unknown4 = state.Unknown4;
|
||||||
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
||||||
@ -103,8 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
float mean = FloatingPointHelper.MeanSquare(channelInput);
|
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
||||||
float newMean = inputMovingAverage.Update(mean, _parameter.InputGain);
|
|
||||||
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||||
float z = 1.0f;
|
float z = 1.0f;
|
||||||
|
|
||||||
@ -123,7 +111,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (y >= state.Unknown14)
|
if (y >= state.Unknown14)
|
||||||
{
|
{
|
||||||
tmpGain = ((1.0f / _parameter.Ratio) - 1.0f) * (y - _parameter.Threshold);
|
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -138,7 +126,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if ((unknown4 - z) <= 0.08f)
|
if ((unknown4 - z) <= 0.08f)
|
||||||
{
|
{
|
||||||
compressionEmaAlpha = _parameter.ReleaseCoefficient;
|
compressionEmaAlpha = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if ((unknown4 - z) >= -0.08f)
|
if ((unknown4 - z) >= -0.08f)
|
||||||
{
|
{
|
||||||
@ -152,31 +140,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
compressionEmaAlpha = _parameter.AttackCoefficient;
|
compressionEmaAlpha = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||||
{
|
{
|
||||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
unknown4 = unknown4New;
|
unknown4 = unknown4New;
|
||||||
previousCompressionEmaAlpha = compressionEmaAlpha;
|
previousCompressionEmaAlpha = compressionEmaAlpha;
|
||||||
|
|
||||||
if (!ResultState.IsEmpty)
|
|
||||||
{
|
|
||||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
|
|
||||||
|
|
||||||
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
|
|
||||||
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
|
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
|
||||||
{
|
|
||||||
statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.InputMovingAverage = inputMovingAverage;
|
state.InputMovingAverage = inputMovingAverage;
|
||||||
@ -186,7 +161,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (IsEffectEnabled)
|
if (IsEffectEnabled)
|
||||||
{
|
{
|
||||||
if (_parameter.Status == UsageState.Invalid)
|
if (Parameter.Status == UsageState.Invalid)
|
||||||
{
|
{
|
||||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||||
}
|
}
|
||||||
else if (_parameter.Status == UsageState.New)
|
else if (Parameter.Status == UsageState.New)
|
||||||
{
|
{
|
||||||
LimiterState.UpdateParameter(ref _parameter);
|
LimiterState.UpdateParameter(ref _parameter);
|
||||||
}
|
}
|
||||||
@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
||||||
{
|
{
|
||||||
Debug.Assert(_parameter.IsChannelCountValid());
|
Debug.Assert(Parameter.IsChannelCountValid());
|
||||||
|
|
||||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||||
{
|
{
|
||||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||||
{
|
{
|
||||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||||
|
|
||||||
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
|
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
|
||||||
|
|
||||||
float sampleInputMax = Math.Abs(inputSample);
|
float sampleInputMax = Math.Abs(inputSample);
|
||||||
|
|
||||||
float inputCoefficient = _parameter.ReleaseCoefficient;
|
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = _parameter.AttackCoefficient;
|
inputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
if (detectorValue > _parameter.Threshold)
|
if (detectorValue > Parameter.Threshold)
|
||||||
{
|
{
|
||||||
attenuation = _parameter.Threshold / detectorValue;
|
attenuation = Parameter.Threshold / detectorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float outputCoefficient = _parameter.ReleaseCoefficient;
|
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = _parameter.AttackCoefficient;
|
outputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||||
|
|
||||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||||
|
|
||||||
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
|
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||||
|
|
||||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||||
|
|
||||||
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
|
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||||
{
|
{
|
||||||
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
|
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (IsEffectEnabled)
|
if (IsEffectEnabled)
|
||||||
{
|
{
|
||||||
if (_parameter.Status == UsageState.Invalid)
|
if (Parameter.Status == UsageState.Invalid)
|
||||||
{
|
{
|
||||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||||
}
|
}
|
||||||
else if (_parameter.Status == UsageState.New)
|
else if (Parameter.Status == UsageState.New)
|
||||||
{
|
{
|
||||||
LimiterState.UpdateParameter(ref _parameter);
|
LimiterState.UpdateParameter(ref _parameter);
|
||||||
}
|
}
|
||||||
@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
||||||
{
|
{
|
||||||
Debug.Assert(_parameter.IsChannelCountValid());
|
Debug.Assert(Parameter.IsChannelCountValid());
|
||||||
|
|
||||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
|
if (!ResultState.IsEmpty && Parameter.StatisticsReset)
|
||||||
{
|
{
|
||||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||||
|
|
||||||
statistics.Reset();
|
statistics.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||||
{
|
{
|
||||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||||
{
|
{
|
||||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||||
|
|
||||||
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
|
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
|
||||||
|
|
||||||
float sampleInputMax = Math.Abs(inputSample);
|
float sampleInputMax = Math.Abs(inputSample);
|
||||||
|
|
||||||
float inputCoefficient = _parameter.ReleaseCoefficient;
|
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = _parameter.AttackCoefficient;
|
inputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
if (detectorValue > _parameter.Threshold)
|
if (detectorValue > Parameter.Threshold)
|
||||||
{
|
{
|
||||||
attenuation = _parameter.Threshold / detectorValue;
|
attenuation = Parameter.Threshold / detectorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float outputCoefficient = _parameter.ReleaseCoefficient;
|
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = _parameter.AttackCoefficient;
|
outputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||||
|
|
||||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||||
|
|
||||||
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
|
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||||
|
|
||||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||||
|
|
||||||
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
|
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||||
{
|
{
|
||||||
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
|
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ResultState.IsEmpty)
|
if (!ResultState.IsEmpty)
|
||||||
@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
{
|
{
|
||||||
|
@ -90,16 +90,9 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|||||||
public bool MakeupGainEnabled;
|
public bool MakeupGainEnabled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicate if the compressor effect should output statistics.
|
/// Reserved/padding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
private Array2<byte> _reserved;
|
||||||
public bool StatisticsEnabled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicate to the DSP that the user did a statistics reset.
|
|
||||||
/// </summary>
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool StatisticsReset;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the <see cref="ChannelCount"/> is valid.
|
/// Check if the <see cref="ChannelCount"/> is valid.
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Effect result state for <seealso cref="Common.EffectType.Compressor"/>.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
||||||
public struct CompressorStatistics
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum input mean value since last reset.
|
|
||||||
/// </summary>
|
|
||||||
public float MaximumMean;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Minimum output gain since last reset.
|
|
||||||
/// </summary>
|
|
||||||
public float MinimumGain;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Last processed input sample, per channel.
|
|
||||||
/// </summary>
|
|
||||||
public Array6<float> LastSamples;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reset the statistics.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channelCount">Number of channels to reset.</param>
|
|
||||||
public void Reset(ushort channelCount)
|
|
||||||
{
|
|
||||||
MaximumMean = 0.0f;
|
|
||||||
MinimumGain = 1.0f;
|
|
||||||
LastSamples.AsSpan()[..channelCount].Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,11 +28,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsUsed { get; }
|
bool IsUsed { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to true to force resetting the previous mix volumes.
|
|
||||||
/// </summary>
|
|
||||||
bool ResetPrevVolume { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mix buffer volumes.
|
/// Mix buffer volumes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -37,16 +37,10 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsUsed;
|
public bool IsUsed;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to true to force resetting the previous mix volumes.
|
|
||||||
/// </summary>
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool ResetPrevVolume;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved/padding.
|
/// Reserved/padding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe fixed byte _reserved[2];
|
private unsafe fixed byte _reserved[3];
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||||
private struct MixArray { }
|
private struct MixArray { }
|
||||||
@ -64,7 +58,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
||||||
|
|
||||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||||
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected constant of any input header.
|
/// The expected constant of any input header.
|
||||||
|
@ -42,16 +42,10 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsUsed;
|
public bool IsUsed;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to true to force resetting the previous mix volumes.
|
|
||||||
/// </summary>
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public bool ResetPrevVolume;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved/padding.
|
/// Reserved/padding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe fixed byte _reserved[10];
|
private unsafe fixed byte _reserved[11];
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||||
private struct MixArray { }
|
private struct MixArray { }
|
||||||
@ -69,7 +63,6 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
||||||
|
|
||||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||||
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected constant of any input header.
|
/// The expected constant of any input header.
|
||||||
|
@ -108,18 +108,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <remarks>This was added in system update 17.0.0</remarks>
|
/// <remarks>This was added in system update 17.0.0</remarks>
|
||||||
public const int Revision12 = 12 << 24;
|
public const int Revision12 = 12 << 24;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// REV13:
|
|
||||||
/// The compressor effect can now output statistics.
|
|
||||||
/// Splitter destinations now explicitly reset the previous mix volume, instead of doing so on first use.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This was added in system update 18.0.0</remarks>
|
|
||||||
public const int Revision13 = 13 << 24;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Last revision supported by the implementation.
|
/// Last revision supported by the implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int LastRevision = Revision13;
|
public const int LastRevision = Revision12;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Target revision magic supported by the implementation.
|
/// Target revision magic supported by the implementation.
|
||||||
@ -392,15 +384,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
|
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if the audio renderer should support explicit previous mix volume reset on splitter.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the audio renderer support explicit previous mix volume reset on splitter</returns>
|
|
||||||
public bool IsSplitterPrevVolumeResetSupported()
|
|
||||||
{
|
|
||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -583,20 +583,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||||
/// Generate a new <see cref="CompressorCommand"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bufferOffset">The target buffer offset.</param>
|
|
||||||
/// <param name="parameter">The compressor parameter.</param>
|
|
||||||
/// <param name="state">The compressor state.</param>
|
|
||||||
/// <param name="effectResultState">The DSP effect result state.</param>
|
|
||||||
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
|
||||||
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> effectResultState, bool isEnabled, int nodeId)
|
|
||||||
{
|
{
|
||||||
if (parameter.IsChannelCountValid())
|
if (parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId);
|
CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
|
@ -735,26 +735,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId)
|
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Compressor);
|
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||||
|
|
||||||
Memory<EffectResultState> dspResultState;
|
|
||||||
|
|
||||||
if (effect.Parameter.StatisticsEnabled)
|
|
||||||
{
|
|
||||||
dspResultState = _effectContext.GetDspStateMemory(effectId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dspResultState = Memory<EffectResultState>.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
_commandBuffer.GenerateCompressorEffect(
|
_commandBuffer.GenerateCompressorEffect(
|
||||||
bufferOffset,
|
bufferOffset,
|
||||||
effect.Parameter,
|
effect.Parameter,
|
||||||
effect.State,
|
effect.State,
|
||||||
dspResultState,
|
|
||||||
effect.IsEnabled,
|
effect.IsEnabled,
|
||||||
nodeId);
|
nodeId);
|
||||||
}
|
}
|
||||||
@ -807,7 +795,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
||||||
break;
|
break;
|
||||||
case EffectType.Compressor:
|
case EffectType.Compressor:
|
||||||
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId);
|
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
||||||
|
@ -169,28 +169,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
if (command.Enabled)
|
if (command.Enabled)
|
||||||
{
|
{
|
||||||
if (command.Parameter.StatisticsEnabled)
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
return command.Parameter.ChannelCount switch
|
1 => 34431,
|
||||||
{
|
2 => 44253,
|
||||||
1 => 22100,
|
4 => 63827,
|
||||||
2 => 33211,
|
6 => 83361,
|
||||||
4 => 41587,
|
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||||
6 => 58819,
|
};
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return command.Parameter.ChannelCount switch
|
|
||||||
{
|
|
||||||
1 => 19052,
|
|
||||||
2 => 29852,
|
|
||||||
4 => 37904,
|
|
||||||
6 => 55020,
|
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
@ -205,28 +191,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
if (command.Enabled)
|
if (command.Enabled)
|
||||||
{
|
{
|
||||||
if (command.Parameter.StatisticsEnabled)
|
return command.Parameter.ChannelCount switch
|
||||||
{
|
{
|
||||||
return command.Parameter.ChannelCount switch
|
1 => 51095,
|
||||||
{
|
2 => 65693,
|
||||||
1 => 32518,
|
4 => 95383,
|
||||||
2 => 49102,
|
6 => 124510,
|
||||||
4 => 61685,
|
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||||
6 => 87250,
|
};
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return command.Parameter.ChannelCount switch
|
|
||||||
{
|
|
||||||
1 => 27963,
|
|
||||||
2 => 44016,
|
|
||||||
4 => 56183,
|
|
||||||
6 => 81862,
|
|
||||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.Parameter.ChannelCount switch
|
return command.Parameter.ChannelCount switch
|
||||||
|
@ -62,19 +62,6 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||||||
UpdateUsageStateForCommandGeneration();
|
UpdateUsageStateForCommandGeneration();
|
||||||
|
|
||||||
Parameter.Status = UsageState.Enabled;
|
Parameter.Status = UsageState.Enabled;
|
||||||
Parameter.StatisticsReset = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void InitializeResultState(ref EffectResultState state)
|
|
||||||
{
|
|
||||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(state.SpecificData)[0];
|
|
||||||
|
|
||||||
statistics.Reset(Parameter.ChannelCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState)
|
|
||||||
{
|
|
||||||
destState = srcState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,6 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBugFixed { get; private set; }
|
public bool IsBugFixed { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSplitterPrevVolumeResetSupported { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize <see cref="SplitterContext"/>.
|
/// Initialize <see cref="SplitterContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -144,8 +139,6 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported();
|
|
||||||
|
|
||||||
SplitterState.InitializeSplitters(splitters.Span);
|
SplitterState.InitializeSplitters(splitters.Span);
|
||||||
|
|
||||||
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
||||||
@ -284,7 +277,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
{
|
{
|
||||||
SplitterDestination destination = GetDestination(parameter.Id);
|
SplitterDestination destination = GetDestination(parameter.Id);
|
||||||
|
|
||||||
destination.Update(parameter, IsSplitterPrevVolumeResetSupported);
|
destination.Update(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -184,16 +184,15 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
/// Update the splitter destination data from user parameter.
|
/// Update the splitter destination data from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
|
||||||
{
|
{
|
||||||
if (Unsafe.IsNullRef(ref _v2))
|
if (Unsafe.IsNullRef(ref _v2))
|
||||||
{
|
{
|
||||||
_v1.Update(parameter, isPrevVolumeResetSupported);
|
_v1.Update(parameter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_v2.Update(parameter, isPrevVolumeResetSupported);
|
_v2.Update(parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
|
||||||
{
|
{
|
||||||
Debug.Assert(Id == parameter.Id);
|
Debug.Assert(Id == parameter.Id);
|
||||||
|
|
||||||
@ -104,8 +103,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
|
|
||||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||||
|
|
||||||
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
if (!IsUsed && parameter.IsUsed)
|
||||||
if (resetPrevVolume)
|
|
||||||
{
|
{
|
||||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
|
|
||||||
|
@ -98,8 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
|
||||||
{
|
{
|
||||||
Debug.Assert(Id == parameter.Id);
|
Debug.Assert(Id == parameter.Id);
|
||||||
|
|
||||||
@ -111,8 +110,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
|
|
||||||
_biquadFilters = parameter.BiquadFilters;
|
_biquadFilters = parameter.BiquadFilters;
|
||||||
|
|
||||||
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
if (!IsUsed && parameter.IsUsed)
|
||||||
if (resetPrevVolume)
|
|
||||||
{
|
{
|
||||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||||
|
|
||||||
|
@ -71,8 +71,6 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
public readonly int GatherBiasPrecision;
|
public readonly int GatherBiasPrecision;
|
||||||
|
|
||||||
public readonly ulong MaximumGpuMemory;
|
|
||||||
|
|
||||||
public Capabilities(
|
public Capabilities(
|
||||||
TargetApi api,
|
TargetApi api,
|
||||||
string vendorName,
|
string vendorName,
|
||||||
@ -133,8 +131,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
int shaderSubgroupSize,
|
int shaderSubgroupSize,
|
||||||
int storageBufferOffsetAlignment,
|
int storageBufferOffsetAlignment,
|
||||||
int textureBufferOffsetAlignment,
|
int textureBufferOffsetAlignment,
|
||||||
int gatherBiasPrecision,
|
int gatherBiasPrecision)
|
||||||
ulong maximumGpuMemory)
|
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
VendorName = vendorName;
|
VendorName = vendorName;
|
||||||
@ -196,7 +193,6 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||||
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
||||||
GatherBiasPrecision = gatherBiasPrecision;
|
GatherBiasPrecision = gatherBiasPrecision;
|
||||||
MaximumGpuMemory = maximumGpuMemory;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
{
|
{
|
||||||
public interface IImageArray : IDisposable
|
public interface IImageArray : IDisposable
|
||||||
{
|
{
|
||||||
|
void SetFormats(int index, Format[] imageFormats);
|
||||||
void SetImages(int index, ITexture[] images);
|
void SetImages(int index, ITexture[] images);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
||||||
|
|
||||||
void SetImage(ShaderStage stage, int binding, ITexture texture);
|
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
|
||||||
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
void SetImageArray(ShaderStage stage, int binding, IImageArray array);
|
||||||
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
|
void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
@ -18,30 +18,30 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
PinnedSpan<byte> GetData(int layer, int level);
|
PinnedSpan<byte> GetData(int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
void SetData(MemoryOwner<byte> data);
|
void SetData(IMemoryOwner<byte> data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
void SetData(MemoryOwner<byte> data, int layer, int level);
|
void SetData(IMemoryOwner<byte> data, int layer, int level);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the texture data. The data passed as a <see cref="MemoryOwner{Byte}" /> will be disposed when
|
/// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
|
||||||
/// the operation completes.
|
/// the operation completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Texture data bytes</param>
|
/// <param name="data">Texture data bytes</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
/// <param name="region">Target sub-region of the texture to update</param>
|
/// <param name="region">Target sub-region of the texture to update</param>
|
||||||
void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
|
||||||
|
|
||||||
void SetStorage(BufferRange buffer);
|
void SetStorage(BufferRange buffer);
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
Register<CounterEventFlushCommand>(CommandType.CounterEventFlush);
|
||||||
|
|
||||||
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
Register<ImageArrayDisposeCommand>(CommandType.ImageArrayDispose);
|
||||||
|
Register<ImageArraySetFormatsCommand>(CommandType.ImageArraySetFormats);
|
||||||
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
Register<ImageArraySetImagesCommand>(CommandType.ImageArraySetImages);
|
||||||
|
|
||||||
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
Register<ProgramDisposeCommand>(CommandType.ProgramDispose);
|
||||||
|
@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
CounterEventFlush,
|
CounterEventFlush,
|
||||||
|
|
||||||
ImageArrayDispose,
|
ImageArrayDispose,
|
||||||
|
ImageArraySetFormats,
|
||||||
ImageArraySetImages,
|
ImageArraySetImages,
|
||||||
|
|
||||||
ProgramDispose,
|
ProgramDispose,
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
|
||||||
|
{
|
||||||
|
struct ImageArraySetFormatsCommand : IGALCommand, IGALCommand<ImageArraySetFormatsCommand>
|
||||||
|
{
|
||||||
|
public readonly CommandType CommandType => CommandType.ImageArraySetFormats;
|
||||||
|
private TableRef<ThreadedImageArray> _imageArray;
|
||||||
|
private int _index;
|
||||||
|
private TableRef<Format[]> _imageFormats;
|
||||||
|
|
||||||
|
public void Set(TableRef<ThreadedImageArray> imageArray, int index, TableRef<Format[]> imageFormats)
|
||||||
|
{
|
||||||
|
_imageArray = imageArray;
|
||||||
|
_index = index;
|
||||||
|
_imageFormats = imageFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(ref ImageArraySetFormatsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
|
{
|
||||||
|
ThreadedImageArray imageArray = command._imageArray.Get(threaded);
|
||||||
|
imageArray.Base.SetFormats(command._index, command._imageFormats.Get(threaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,17 +10,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
private ShaderStage _stage;
|
private ShaderStage _stage;
|
||||||
private int _binding;
|
private int _binding;
|
||||||
private TableRef<ITexture> _texture;
|
private TableRef<ITexture> _texture;
|
||||||
|
private Format _imageFormat;
|
||||||
|
|
||||||
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture)
|
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
_stage = stage;
|
_stage = stage;
|
||||||
_binding = binding;
|
_binding = binding;
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
|
_imageFormat = imageFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base);
|
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
|||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetData;
|
public readonly CommandType CommandType => CommandType.TextureSetData;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
|||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
||||||
{
|
{
|
||||||
@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
|
|||||||
{
|
{
|
||||||
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
|
||||||
private TableRef<ThreadedTexture> _texture;
|
private TableRef<ThreadedTexture> _texture;
|
||||||
private TableRef<MemoryOwner<byte>> _data;
|
private TableRef<IMemoryOwner<byte>> _data;
|
||||||
private int _layer;
|
private int _layer;
|
||||||
private int _level;
|
private int _level;
|
||||||
private Rectangle<int> _region;
|
private Rectangle<int> _region;
|
||||||
|
|
||||||
public void Set(TableRef<ThreadedTexture> texture, TableRef<MemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
_data = data;
|
_data = data;
|
||||||
|
@ -27,6 +27,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetFormats(int index, Format[] imageFormats)
|
||||||
|
{
|
||||||
|
_renderer.New<ImageArraySetFormatsCommand>().Set(Ref(this), index, Ref(imageFormats));
|
||||||
|
_renderer.QueueCommand();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
_renderer.New<ImageArraySetImagesCommand>().Set(Ref(this), index, Ref(images));
|
_renderer.New<ImageArraySetImagesCommand>().Set(Ref(this), index, Ref(images));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
||||||
{
|
{
|
||||||
@ -111,21 +111,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
|
@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture));
|
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,5 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
Bilinear,
|
Bilinear,
|
||||||
Nearest,
|
Nearest,
|
||||||
Fsr,
|
Fsr,
|
||||||
Area,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -353,7 +353,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> data;
|
IMemoryOwner<byte> data;
|
||||||
if (srcLinear)
|
if (srcLinear)
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
@ -62,51 +61,51 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="format">Shader image format</param>
|
/// <param name="format">Shader image format</param>
|
||||||
/// <returns>Texture format</returns>
|
/// <returns>Texture format</returns>
|
||||||
public static FormatInfo GetFormatInfo(TextureFormat format)
|
public static Format GetFormat(TextureFormat format)
|
||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE0055 // Disable formatting
|
#pragma warning disable IDE0055 // Disable formatting
|
||||||
TextureFormat.R8Unorm => new(Format.R8Unorm, 1, 1, 1, 1),
|
TextureFormat.R8Unorm => Format.R8Unorm,
|
||||||
TextureFormat.R8Snorm => new(Format.R8Snorm, 1, 1, 1, 1),
|
TextureFormat.R8Snorm => Format.R8Snorm,
|
||||||
TextureFormat.R8Uint => new(Format.R8Uint, 1, 1, 1, 1),
|
TextureFormat.R8Uint => Format.R8Uint,
|
||||||
TextureFormat.R8Sint => new(Format.R8Sint, 1, 1, 1, 1),
|
TextureFormat.R8Sint => Format.R8Sint,
|
||||||
TextureFormat.R16Float => new(Format.R16Float, 1, 1, 2, 1),
|
TextureFormat.R16Float => Format.R16Float,
|
||||||
TextureFormat.R16Unorm => new(Format.R16Unorm, 1, 1, 2, 1),
|
TextureFormat.R16Unorm => Format.R16Unorm,
|
||||||
TextureFormat.R16Snorm => new(Format.R16Snorm, 1, 1, 2, 1),
|
TextureFormat.R16Snorm => Format.R16Snorm,
|
||||||
TextureFormat.R16Uint => new(Format.R16Uint, 1, 1, 2, 1),
|
TextureFormat.R16Uint => Format.R16Uint,
|
||||||
TextureFormat.R16Sint => new(Format.R16Sint, 1, 1, 2, 1),
|
TextureFormat.R16Sint => Format.R16Sint,
|
||||||
TextureFormat.R32Float => new(Format.R32Float, 1, 1, 4, 1),
|
TextureFormat.R32Float => Format.R32Float,
|
||||||
TextureFormat.R32Uint => new(Format.R32Uint, 1, 1, 4, 1),
|
TextureFormat.R32Uint => Format.R32Uint,
|
||||||
TextureFormat.R32Sint => new(Format.R32Sint, 1, 1, 4, 1),
|
TextureFormat.R32Sint => Format.R32Sint,
|
||||||
TextureFormat.R8G8Unorm => new(Format.R8G8Unorm, 1, 1, 2, 2),
|
TextureFormat.R8G8Unorm => Format.R8G8Unorm,
|
||||||
TextureFormat.R8G8Snorm => new(Format.R8G8Snorm, 1, 1, 2, 2),
|
TextureFormat.R8G8Snorm => Format.R8G8Snorm,
|
||||||
TextureFormat.R8G8Uint => new(Format.R8G8Uint, 1, 1, 2, 2),
|
TextureFormat.R8G8Uint => Format.R8G8Uint,
|
||||||
TextureFormat.R8G8Sint => new(Format.R8G8Sint, 1, 1, 2, 2),
|
TextureFormat.R8G8Sint => Format.R8G8Sint,
|
||||||
TextureFormat.R16G16Float => new(Format.R16G16Float, 1, 1, 4, 2),
|
TextureFormat.R16G16Float => Format.R16G16Float,
|
||||||
TextureFormat.R16G16Unorm => new(Format.R16G16Unorm, 1, 1, 4, 2),
|
TextureFormat.R16G16Unorm => Format.R16G16Unorm,
|
||||||
TextureFormat.R16G16Snorm => new(Format.R16G16Snorm, 1, 1, 4, 2),
|
TextureFormat.R16G16Snorm => Format.R16G16Snorm,
|
||||||
TextureFormat.R16G16Uint => new(Format.R16G16Uint, 1, 1, 4, 2),
|
TextureFormat.R16G16Uint => Format.R16G16Uint,
|
||||||
TextureFormat.R16G16Sint => new(Format.R16G16Sint, 1, 1, 4, 2),
|
TextureFormat.R16G16Sint => Format.R16G16Sint,
|
||||||
TextureFormat.R32G32Float => new(Format.R32G32Float, 1, 1, 8, 2),
|
TextureFormat.R32G32Float => Format.R32G32Float,
|
||||||
TextureFormat.R32G32Uint => new(Format.R32G32Uint, 1, 1, 8, 2),
|
TextureFormat.R32G32Uint => Format.R32G32Uint,
|
||||||
TextureFormat.R32G32Sint => new(Format.R32G32Sint, 1, 1, 8, 2),
|
TextureFormat.R32G32Sint => Format.R32G32Sint,
|
||||||
TextureFormat.R8G8B8A8Unorm => new(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
|
TextureFormat.R8G8B8A8Unorm => Format.R8G8B8A8Unorm,
|
||||||
TextureFormat.R8G8B8A8Snorm => new(Format.R8G8B8A8Snorm, 1, 1, 4, 4),
|
TextureFormat.R8G8B8A8Snorm => Format.R8G8B8A8Snorm,
|
||||||
TextureFormat.R8G8B8A8Uint => new(Format.R8G8B8A8Uint, 1, 1, 4, 4),
|
TextureFormat.R8G8B8A8Uint => Format.R8G8B8A8Uint,
|
||||||
TextureFormat.R8G8B8A8Sint => new(Format.R8G8B8A8Sint, 1, 1, 4, 4),
|
TextureFormat.R8G8B8A8Sint => Format.R8G8B8A8Sint,
|
||||||
TextureFormat.R16G16B16A16Float => new(Format.R16G16B16A16Float, 1, 1, 8, 4),
|
TextureFormat.R16G16B16A16Float => Format.R16G16B16A16Float,
|
||||||
TextureFormat.R16G16B16A16Unorm => new(Format.R16G16B16A16Unorm, 1, 1, 8, 4),
|
TextureFormat.R16G16B16A16Unorm => Format.R16G16B16A16Unorm,
|
||||||
TextureFormat.R16G16B16A16Snorm => new(Format.R16G16B16A16Snorm, 1, 1, 8, 4),
|
TextureFormat.R16G16B16A16Snorm => Format.R16G16B16A16Snorm,
|
||||||
TextureFormat.R16G16B16A16Uint => new(Format.R16G16B16A16Uint, 1, 1, 8, 4),
|
TextureFormat.R16G16B16A16Uint => Format.R16G16B16A16Uint,
|
||||||
TextureFormat.R16G16B16A16Sint => new(Format.R16G16B16A16Sint, 1, 1, 8, 4),
|
TextureFormat.R16G16B16A16Sint => Format.R16G16B16A16Sint,
|
||||||
TextureFormat.R32G32B32A32Float => new(Format.R32G32B32A32Float, 1, 1, 16, 4),
|
TextureFormat.R32G32B32A32Float => Format.R32G32B32A32Float,
|
||||||
TextureFormat.R32G32B32A32Uint => new(Format.R32G32B32A32Uint, 1, 1, 16, 4),
|
TextureFormat.R32G32B32A32Uint => Format.R32G32B32A32Uint,
|
||||||
TextureFormat.R32G32B32A32Sint => new(Format.R32G32B32A32Sint, 1, 1, 16, 4),
|
TextureFormat.R32G32B32A32Sint => Format.R32G32B32A32Sint,
|
||||||
TextureFormat.R10G10B10A2Unorm => new(Format.R10G10B10A2Unorm, 1, 1, 4, 4),
|
TextureFormat.R10G10B10A2Unorm => Format.R10G10B10A2Unorm,
|
||||||
TextureFormat.R10G10B10A2Uint => new(Format.R10G10B10A2Uint, 1, 1, 4, 4),
|
TextureFormat.R10G10B10A2Uint => Format.R10G10B10A2Uint,
|
||||||
TextureFormat.R11G11B10Float => new(Format.R11G11B10Float, 1, 1, 4, 3),
|
TextureFormat.R11G11B10Float => Format.R11G11B10Float,
|
||||||
_ => FormatInfo.Invalid,
|
_ => 0,
|
||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -47,11 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
private const int MinCountForDeletion = 32;
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
|
private const ulong MaxTextureSizeCapacity = 1024 * 1024 * 1024; // MB;
|
||||||
private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024;
|
|
||||||
private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024;
|
|
||||||
private const float MemoryScaleFactor = 0.50f;
|
|
||||||
private ulong _maxCacheMemoryUsage = 0;
|
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private ulong _totalSize;
|
private ulong _totalSize;
|
||||||
@ -61,25 +56,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="context">The GPU context that the cache belongs to</param>
|
|
||||||
public void Initialize(GpuContext context)
|
|
||||||
{
|
|
||||||
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
|
||||||
|
|
||||||
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
|
|
||||||
|
|
||||||
if (context.Capabilities.MaximumGpuMemory == 0)
|
|
||||||
{
|
|
||||||
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the automatic deletion cache.
|
/// Creates a new instance of the automatic deletion cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -109,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity ||
|
if (_textures.Count > MaxCapacity ||
|
||||||
(_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion))
|
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
RemoveLeastUsedTexture();
|
RemoveLeastUsedTexture();
|
||||||
}
|
}
|
||||||
@ -134,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_textures.AddLast(texture.CacheNode);
|
_textures.AddLast(texture.CacheNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion)
|
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||||
{
|
{
|
||||||
RemoveLeastUsedTexture();
|
RemoveLeastUsedTexture();
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
readonly struct FormatInfo
|
readonly struct FormatInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// An invalid texture format.
|
|
||||||
/// </summary>
|
|
||||||
public static FormatInfo Invalid { get; } = new(0, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default, generic RGBA8 texture format.
|
/// A default, generic RGBA8 texture format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,7 +23,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Must be 1 for non-compressed formats.
|
/// Must be 1 for non-compressed formats.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public byte BlockWidth { get; }
|
public int BlockWidth { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The block height for compressed formats.
|
/// The block height for compressed formats.
|
||||||
@ -36,17 +31,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Must be 1 for non-compressed formats.
|
/// Must be 1 for non-compressed formats.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public byte BlockHeight { get; }
|
public int BlockHeight { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of bytes occupied by a single pixel in memory of the texture data.
|
/// The number of bytes occupied by a single pixel in memory of the texture data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte BytesPerPixel { get; }
|
public int BytesPerPixel { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum number of components this format has defined (in RGBA order).
|
/// The maximum number of components this format has defined (in RGBA order).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Components { get; }
|
public int Components { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
||||||
@ -62,10 +57,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
|
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
|
||||||
public FormatInfo(
|
public FormatInfo(
|
||||||
Format format,
|
Format format,
|
||||||
byte blockWidth,
|
int blockWidth,
|
||||||
byte blockHeight,
|
int blockHeight,
|
||||||
byte bytesPerPixel,
|
int bytesPerPixel,
|
||||||
byte components)
|
int components)
|
||||||
{
|
{
|
||||||
Format = format;
|
Format = format;
|
||||||
BlockWidth = blockWidth;
|
BlockWidth = blockWidth;
|
||||||
|
@ -13,11 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if the sampler has sRGB conversion enabled, false otherwise.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSrgb { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host sampler object.
|
/// Host sampler object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -35,8 +30,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="descriptor">The Maxwell sampler descriptor</param>
|
/// <param name="descriptor">The Maxwell sampler descriptor</param>
|
||||||
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
||||||
{
|
{
|
||||||
IsSrgb = descriptor.UnpackSrgb();
|
|
||||||
|
|
||||||
MinFilter minFilter = descriptor.UnpackMinFilter();
|
MinFilter minFilter = descriptor.UnpackMinFilter();
|
||||||
MagFilter magFilter = descriptor.UnpackMagFilter();
|
MagFilter magFilter = descriptor.UnpackMagFilter();
|
||||||
|
|
||||||
|
@ -113,15 +113,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks the sampler sRGB format flag.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the has sampler is sRGB conversion enabled, false otherwise</returns>
|
|
||||||
public readonly bool UnpackSrgb()
|
|
||||||
{
|
|
||||||
return (Word0 & (1 << 13)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
|
/// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,6 +7,7 @@ using Ryujinx.Graphics.Texture.Astc;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -661,7 +662,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryOwner<byte> result = ConvertToHostCompatibleFormat(data);
|
IMemoryOwner<byte> result = ConvertToHostCompatibleFormat(data);
|
||||||
|
|
||||||
if (ScaleFactor != 1f && AllowScaledSetData())
|
if (ScaleFactor != 1f && AllowScaledSetData())
|
||||||
{
|
{
|
||||||
@ -684,7 +685,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Uploads new texture data to the host GPU.
|
/// Uploads new texture data to the host GPU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
@ -703,7 +704,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="data">New data</param>
|
/// <param name="data">New data</param>
|
||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
@ -721,7 +722,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="layer">Target layer</param>
|
/// <param name="layer">Target layer</param>
|
||||||
/// <param name="level">Target level</param>
|
/// <param name="level">Target level</param>
|
||||||
/// <param name="region">Target sub-region of the texture to update</param>
|
/// <param name="region">Target sub-region of the texture to update</param>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
|
|
||||||
@ -739,7 +740,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="level">Mip level to convert</param>
|
/// <param name="level">Mip level to convert</param>
|
||||||
/// <param name="single">True to convert a single slice</param>
|
/// <param name="single">True to convert a single slice</param>
|
||||||
/// <returns>Converted data</returns>
|
/// <returns>Converted data</returns>
|
||||||
public MemoryOwner<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
public IMemoryOwner<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
|
||||||
{
|
{
|
||||||
int width = Info.Width;
|
int width = Info.Width;
|
||||||
int height = Info.Height;
|
int height = Info.Height;
|
||||||
@ -754,7 +755,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
int sliceDepth = single ? 1 : depth;
|
int sliceDepth = single ? 1 : depth;
|
||||||
|
|
||||||
MemoryOwner<byte> linear;
|
IMemoryOwner<byte> linear;
|
||||||
|
|
||||||
if (Info.IsLinear)
|
if (Info.IsLinear)
|
||||||
{
|
{
|
||||||
@ -787,7 +788,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryOwner<byte> result = linear;
|
IMemoryOwner<byte> result = linear;
|
||||||
|
|
||||||
// Handle compressed cases not supported by the host:
|
// Handle compressed cases not supported by the host:
|
||||||
// - ASTC is usually not supported on desktop cards.
|
// - ASTC is usually not supported on desktop cards.
|
||||||
@ -831,19 +832,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
case Format.Etc2RgbaUnorm:
|
case Format.Etc2RgbaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodeRgba(result.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodeRgba(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Etc2RgbPtaSrgb:
|
case Format.Etc2RgbPtaSrgb:
|
||||||
case Format.Etc2RgbPtaUnorm:
|
case Format.Etc2RgbPtaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodePta(result.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodePta(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Etc2RgbSrgb:
|
case Format.Etc2RgbSrgb:
|
||||||
case Format.Etc2RgbUnorm:
|
case Format.Etc2RgbUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return ETC2Decoder.DecodeRgb(result.Span, width, height, sliceDepth, levels, layers);
|
return ETC2Decoder.DecodeRgb(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,43 +856,43 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
case Format.Bc1RgbaUnorm:
|
case Format.Bc1RgbaUnorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC1(result.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC1(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc2Srgb:
|
case Format.Bc2Srgb:
|
||||||
case Format.Bc2Unorm:
|
case Format.Bc2Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC2(result.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC2(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc3Srgb:
|
case Format.Bc3Srgb:
|
||||||
case Format.Bc3Unorm:
|
case Format.Bc3Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC3(result.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC3(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
case Format.Bc4Snorm:
|
case Format.Bc4Snorm:
|
||||||
case Format.Bc4Unorm:
|
case Format.Bc4Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC4(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
|
return BCnDecoder.DecodeBC4(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
|
||||||
}
|
}
|
||||||
case Format.Bc5Snorm:
|
case Format.Bc5Snorm:
|
||||||
case Format.Bc5Unorm:
|
case Format.Bc5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC5(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
|
return BCnDecoder.DecodeBC5(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
|
||||||
}
|
}
|
||||||
case Format.Bc6HSfloat:
|
case Format.Bc6HSfloat:
|
||||||
case Format.Bc6HUfloat:
|
case Format.Bc6HUfloat:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC6(result.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
|
return BCnDecoder.DecodeBC6(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
|
||||||
}
|
}
|
||||||
case Format.Bc7Srgb:
|
case Format.Bc7Srgb:
|
||||||
case Format.Bc7Unorm:
|
case Format.Bc7Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return BCnDecoder.DecodeBC7(result.Span, width, height, sliceDepth, levels, layers);
|
return BCnDecoder.DecodeBC7(result.Memory.Span, width, height, sliceDepth, levels, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -899,7 +900,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Span, width);
|
var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Memory.Span, width);
|
||||||
|
|
||||||
if (_context.Capabilities.SupportsR4G4B4A4Format)
|
if (_context.Capabilities.SupportsR4G4B4A4Format)
|
||||||
{
|
{
|
||||||
@ -909,7 +910,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
using (converted)
|
using (converted)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Memory.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -920,7 +921,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -932,24 +933,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
case Format.R5G6B5Unorm:
|
case Format.R5G6B5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Span, width);
|
return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width);
|
||||||
}
|
}
|
||||||
case Format.B5G5R5A1Unorm:
|
case Format.B5G5R5A1Unorm:
|
||||||
case Format.R5G5B5X1Unorm:
|
case Format.R5G5B5X1Unorm:
|
||||||
case Format.R5G5B5A1Unorm:
|
case Format.R5G5B5A1Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Span, width, Format == Format.R5G5B5X1Unorm);
|
return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Memory.Span, width, Format == Format.R5G5B5X1Unorm);
|
||||||
}
|
}
|
||||||
case Format.A1B5G5R5Unorm:
|
case Format.A1B5G5R5Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Span, width);
|
return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width);
|
||||||
}
|
}
|
||||||
case Format.R4G4B4A4Unorm:
|
case Format.R4G4B4A4Unorm:
|
||||||
using (result)
|
using (result)
|
||||||
{
|
{
|
||||||
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Span, width);
|
return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// For images, indicates the format specified on the shader.
|
/// For images, indicates the format specified on the shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FormatInfo FormatInfo { get; }
|
public Format Format { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shader texture host set index.
|
/// Shader texture host set index.
|
||||||
@ -58,17 +58,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs the texture binding information structure.
|
/// Constructs the texture binding information structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">The shader sampler target type</param>
|
/// <param name="target">The shader sampler target type</param>
|
||||||
/// <param name="formatInfo">Format of the image as declared on the shader</param>
|
/// <param name="format">Format of the image as declared on the shader</param>
|
||||||
/// <param name="set">Shader texture host set index</param>
|
/// <param name="set">Shader texture host set index</param>
|
||||||
/// <param name="binding">The shader texture binding point</param>
|
/// <param name="binding">The shader texture binding point</param>
|
||||||
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
|
/// <param name="arrayLength">For array of textures, this indicates the length of the array. A value of one indicates it is not an array</param>
|
||||||
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
|
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
|
||||||
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
|
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
|
||||||
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
|
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
|
||||||
public TextureBindingInfo(Target target, FormatInfo formatInfo, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
|
public TextureBindingInfo(Target target, Format format, int set, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
FormatInfo = formatInfo;
|
Format = format;
|
||||||
Set = set;
|
Set = set;
|
||||||
Binding = binding;
|
Binding = binding;
|
||||||
ArrayLength = arrayLength;
|
ArrayLength = arrayLength;
|
||||||
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int cbufSlot,
|
int cbufSlot,
|
||||||
int handle,
|
int handle,
|
||||||
TextureUsageFlags flags,
|
TextureUsageFlags flags,
|
||||||
bool isSamplerOnly) : this(target, FormatInfo.Invalid, set, binding, arrayLength, cbufSlot, handle, flags)
|
bool isSamplerOnly) : this(target, 0, set, binding, arrayLength, cbufSlot, handle, flags)
|
||||||
{
|
{
|
||||||
IsSamplerOnly = isSamplerOnly;
|
IsSamplerOnly = isSamplerOnly;
|
||||||
}
|
}
|
||||||
|
@ -659,6 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
|
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
|
||||||
length = Math.Min(length, bindingInfo.ArrayLength);
|
length = Math.Min(length, bindingInfo.ArrayLength);
|
||||||
|
|
||||||
|
Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
|
||||||
@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture);
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
@ -696,6 +697,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||||
|
|
||||||
|
Format format = bindingInfo.Format;
|
||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
@ -703,15 +706,26 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
{
|
{
|
||||||
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
formats[index] = format;
|
||||||
textures[index] = hostTexture;
|
textures[index] = hostTexture;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -723,6 +737,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
|
entry.ImageArray.SetFormats(0, formats);
|
||||||
entry.ImageArray.SetImages(0, textures);
|
entry.ImageArray.SetImages(0, textures);
|
||||||
|
|
||||||
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
||||||
@ -848,6 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer);
|
entry.UpdateData(cachedTextureBuffer, cachedSamplerBuffer, separateSamplerBuffer);
|
||||||
|
|
||||||
|
Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
|
||||||
@ -867,7 +883,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
samplerId = TextureHandle.UnpackSamplerId(packedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
@ -900,6 +916,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
hostSampler = sampler?.GetHostSampler(texture);
|
hostSampler = sampler?.GetHostSampler(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Format format = bindingInfo.Format;
|
||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
@ -907,15 +925,26 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
{
|
{
|
||||||
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
formats[index] = format;
|
||||||
textures[index] = hostTexture;
|
textures[index] = hostTexture;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -927,6 +956,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
|
entry.ImageArray.SetFormats(0, formats);
|
||||||
entry.ImageArray.SetImages(0, textures);
|
entry.ImageArray.SetImages(0, textures);
|
||||||
|
|
||||||
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
SetImageArray(stage, bindingInfo, entry.ImageArray);
|
||||||
|
@ -187,9 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
(TexturePool texturePool, SamplerPool samplerPool) = GetPools();
|
||||||
|
|
||||||
Sampler sampler = samplerPool?.Get(samplerId);
|
return (texturePool.Get(textureId), samplerPool.Get(samplerId));
|
||||||
|
|
||||||
return (texturePool.Get(textureId, sampler?.IsSrgb ?? true), sampler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -510,12 +508,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
state.SamplerHandle = samplerId;
|
state.SamplerHandle = samplerId;
|
||||||
|
|
||||||
Sampler sampler = samplerPool?.Get(samplerId);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, sampler?.IsSrgb ?? true, out Texture texture);
|
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
||||||
|
|
||||||
|
Sampler sampler = samplerPool?.Get(samplerId);
|
||||||
|
|
||||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||||
|
|
||||||
@ -524,7 +522,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, bindingInfo.Format, false);
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
@ -618,7 +616,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (!poolModified &&
|
if (!poolModified &&
|
||||||
state.TextureHandle == textureId &&
|
state.TextureHandle == textureId &&
|
||||||
state.ImageFormat == bindingInfo.FormatInfo.Format &&
|
|
||||||
state.CachedTexture != null &&
|
state.CachedTexture != null &&
|
||||||
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
|
state.CachedTexture.InvalidatedSequence == state.InvalidatedSequence)
|
||||||
{
|
{
|
||||||
@ -632,22 +629,26 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
cachedTexture.SignalModified();
|
cachedTexture.SignalModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 && UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
|
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||||
|
|
||||||
|
if (state.ImageFormat != format ||
|
||||||
|
((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||||
|
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage)))
|
||||||
{
|
{
|
||||||
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||||
|
|
||||||
state.Texture = hostTextureRebind;
|
state.Texture = hostTextureRebind;
|
||||||
|
state.ImageFormat = format;
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind);
|
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.TextureHandle = textureId;
|
state.TextureHandle = textureId;
|
||||||
state.ImageFormat = bindingInfo.FormatInfo.Format;
|
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, bindingInfo.FormatInfo, out Texture texture);
|
ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
|
||||||
|
|
||||||
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
specStateMatches &= specState.MatchesImage(stage, index, descriptor);
|
||||||
|
|
||||||
@ -659,7 +660,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
|
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
|
Format format = bindingInfo.Format;
|
||||||
|
|
||||||
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, format, true);
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
@ -681,7 +689,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
state.Texture = hostTexture;
|
state.Texture = hostTexture;
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture);
|
Format format = bindingInfo.Format;
|
||||||
|
|
||||||
|
if (format == 0 && texture != null)
|
||||||
|
{
|
||||||
|
format = texture.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.ImageFormat = format;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.CachedTexture = texture;
|
state.CachedTexture = texture;
|
||||||
|
@ -68,14 +68,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_cache = new AutoDeleteCache();
|
_cache = new AutoDeleteCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
|
||||||
/// </summary>
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
_cache.Initialize(_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles marking of textures written to a memory region being (partially) remapped.
|
/// Handles marking of textures written to a memory region being (partially) remapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -739,8 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
return (lhsFormat.Format == Format.R8G8B8A8Unorm && rhsFormat.Format == Format.R32G32B32A32Float) ||
|
||||||
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm) ||
|
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm);
|
||||||
(lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R32Uint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
@ -6,6 +5,7 @@ using Ryujinx.Memory;
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
|
ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
|
||||||
|
|
||||||
MemoryOwner<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
IMemoryOwner<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
||||||
|
|
||||||
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
}
|
}
|
||||||
|
@ -75,76 +75,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
private readonly ConcurrentQueue<DereferenceRequest> _dereferenceQueue = new();
|
||||||
private TextureDescriptor _defaultDescriptor;
|
private TextureDescriptor _defaultDescriptor;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List of textures that shares the same memory region, but have different formats.
|
|
||||||
/// </summary>
|
|
||||||
private class TextureAliasList
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Alias texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Format">Texture format</param>
|
|
||||||
/// <param name="Texture">Texture</param>
|
|
||||||
private readonly record struct Alias(Format Format, Texture Texture);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List of texture aliases.
|
|
||||||
/// </summary>
|
|
||||||
private readonly List<Alias> _aliases;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the texture alias list.
|
|
||||||
/// </summary>
|
|
||||||
public TextureAliasList()
|
|
||||||
{
|
|
||||||
_aliases = new List<Alias>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new texture alias.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format">Alias format</param>
|
|
||||||
/// <param name="texture">Alias texture</param>
|
|
||||||
public void Add(Format format, Texture texture)
|
|
||||||
{
|
|
||||||
_aliases.Add(new Alias(format, texture));
|
|
||||||
texture.IncrementReferenceCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a texture with the requested format, or returns null if not found.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format">Format to find</param>
|
|
||||||
/// <returns>Texture with the requested format, or null if not found</returns>
|
|
||||||
public Texture Find(Format format)
|
|
||||||
{
|
|
||||||
foreach (var alias in _aliases)
|
|
||||||
{
|
|
||||||
if (alias.Format == format)
|
|
||||||
{
|
|
||||||
return alias.Texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all alias textures.
|
|
||||||
/// </summary>
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
foreach (var entry in _aliases)
|
|
||||||
{
|
|
||||||
entry.Texture.DecrementReferenceCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
_aliases.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Dictionary<Texture, TextureAliasList> _aliasLists;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Linked list node used on the texture pool cache.
|
/// Linked list node used on the texture pool cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -165,7 +95,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -186,13 +115,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
|
|
||||||
// The dereference queue can put our texture back on the cache.
|
// The dereference queue can put our texture back on the cache.
|
||||||
if ((texture = ProcessDereferenceQueue(id)) != null)
|
if ((texture = ProcessDereferenceQueue(id)) != null)
|
||||||
{
|
{
|
||||||
return ref descriptor;
|
return ref descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
@ -227,17 +157,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
/// <returns>The texture with the given ID</returns>
|
/// <returns>The texture with the given ID</returns>
|
||||||
public override Texture Get(int id)
|
public override Texture Get(int id)
|
||||||
{
|
|
||||||
return Get(id, srgbSampler: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the texture with the given ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
|
||||||
/// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
|
|
||||||
/// <returns>The texture with the given ID</returns>
|
|
||||||
public Texture Get(int id, bool srgbSampler)
|
|
||||||
{
|
{
|
||||||
if ((uint)id >= Items.Length)
|
if ((uint)id >= Items.Length)
|
||||||
{
|
{
|
||||||
@ -251,7 +170,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
SynchronizeMemory();
|
SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
GetForBinding(id, srgbSampler, out Texture texture);
|
GetInternal(id, out Texture texture);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
@ -263,10 +182,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// This method assumes that the pool has been manually synchronized before doing binding.
|
/// This method assumes that the pool has been manually synchronized before doing binding.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||||
/// <param name="srgbSampler">Whether the texture is being accessed with a sampler that has sRGB conversion enabled</param>
|
|
||||||
/// <param name="texture">The texture with the given ID</param>
|
/// <param name="texture">The texture with the given ID</param>
|
||||||
/// <returns>The texture descriptor with the given ID</returns>
|
/// <returns>The texture descriptor with the given ID</returns>
|
||||||
public ref readonly TextureDescriptor GetForBinding(int id, bool srgbSampler, out Texture texture)
|
public ref readonly TextureDescriptor GetForBinding(int id, out Texture texture)
|
||||||
{
|
{
|
||||||
if ((uint)id >= Items.Length)
|
if ((uint)id >= Items.Length)
|
||||||
{
|
{
|
||||||
@ -276,66 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
// When getting for binding, assume the pool has already been synchronized.
|
// When getting for binding, assume the pool has already been synchronized.
|
||||||
|
|
||||||
if (!srgbSampler)
|
|
||||||
{
|
|
||||||
// If the sampler does not have the sRGB bit enabled, then the texture can't use a sRGB format.
|
|
||||||
ref readonly TextureDescriptor tempDescriptor = ref GetDescriptorRef(id);
|
|
||||||
|
|
||||||
if (tempDescriptor.UnpackSrgb() && FormatTable.TryGetTextureFormat(tempDescriptor.UnpackFormat(), isSrgb: false, out FormatInfo formatInfo))
|
|
||||||
{
|
|
||||||
// Get a view of the texture with the right format.
|
|
||||||
return ref GetForBinding(id, formatInfo, out texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref GetInternal(id, out texture);
|
return ref GetInternal(id, out texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the texture descriptor and texture with the given ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method assumes that the pool has been manually synchronized before doing binding.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
|
||||||
/// <param name="formatInfo">Texture format information</param>
|
|
||||||
/// <param name="texture">The texture with the given ID</param>
|
|
||||||
/// <returns>The texture descriptor with the given ID</returns>
|
|
||||||
public ref readonly TextureDescriptor GetForBinding(int id, FormatInfo formatInfo, out Texture texture)
|
|
||||||
{
|
|
||||||
if ((uint)id >= Items.Length)
|
|
||||||
{
|
|
||||||
texture = null;
|
|
||||||
return ref _defaultDescriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref GetInternal(id, out texture);
|
|
||||||
|
|
||||||
if (texture != null && formatInfo.Format != 0 && texture.Format != formatInfo.Format)
|
|
||||||
{
|
|
||||||
if (!_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
|
|
||||||
{
|
|
||||||
_aliasLists.Add(texture, aliasList = new TextureAliasList());
|
|
||||||
}
|
|
||||||
|
|
||||||
texture = aliasList.Find(formatInfo.Format);
|
|
||||||
|
|
||||||
if (texture == null)
|
|
||||||
{
|
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
|
||||||
info = ChangeFormat(info, formatInfo);
|
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
|
||||||
|
|
||||||
if (texture != null)
|
|
||||||
{
|
|
||||||
aliasList.Add(formatInfo.Format, texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
/// Checks if the pool was modified, and returns the last sequence number where a modification was detected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -373,7 +234,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
RemoveAliasList(texture);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,8 +327,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
texture.DecrementReferenceCount();
|
texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveAliasList(texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -511,7 +369,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (Interlocked.Exchange(ref Items[id], null) != null)
|
if (Interlocked.Exchange(ref Items[id], null) != null)
|
||||||
{
|
{
|
||||||
texture.DecrementReferenceCount(this, id);
|
texture.DecrementReferenceCount(this, id);
|
||||||
RemoveAliasList(texture);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -765,57 +622,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
component == SwizzleComponent.Green;
|
component == SwizzleComponent.Green;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the format on the texture information structure, and also adjusts the width for the new format if needed.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Texture information</param>
|
|
||||||
/// <param name="dstFormat">New format</param>
|
|
||||||
/// <returns>Texture information with the new format</returns>
|
|
||||||
private static TextureInfo ChangeFormat(in TextureInfo info, FormatInfo dstFormat)
|
|
||||||
{
|
|
||||||
int width = info.Width;
|
|
||||||
|
|
||||||
if (info.FormatInfo.BytesPerPixel != dstFormat.BytesPerPixel)
|
|
||||||
{
|
|
||||||
int stride = width * info.FormatInfo.BytesPerPixel;
|
|
||||||
width = stride / dstFormat.BytesPerPixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TextureInfo(
|
|
||||||
info.GpuAddress,
|
|
||||||
width,
|
|
||||||
info.Height,
|
|
||||||
info.DepthOrLayers,
|
|
||||||
info.Levels,
|
|
||||||
info.SamplesInX,
|
|
||||||
info.SamplesInY,
|
|
||||||
info.Stride,
|
|
||||||
info.IsLinear,
|
|
||||||
info.GobBlocksInY,
|
|
||||||
info.GobBlocksInZ,
|
|
||||||
info.GobBlocksInTileX,
|
|
||||||
info.Target,
|
|
||||||
dstFormat,
|
|
||||||
info.DepthStencilMode,
|
|
||||||
info.SwizzleR,
|
|
||||||
info.SwizzleG,
|
|
||||||
info.SwizzleB,
|
|
||||||
info.SwizzleA);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all aliases for a texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">Texture to have the aliases removed</param>
|
|
||||||
private void RemoveAliasList(Texture texture)
|
|
||||||
{
|
|
||||||
if (_aliasLists.TryGetValue(texture, out TextureAliasList aliasList))
|
|
||||||
{
|
|
||||||
_aliasLists.Remove(texture);
|
|
||||||
aliasList.Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decrements the reference count of the texture.
|
/// Decrements the reference count of the texture.
|
||||||
/// This indicates that the texture pool is not using it anymore.
|
/// This indicates that the texture pool is not using it anymore.
|
||||||
@ -823,11 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="item">The texture to be deleted</param>
|
/// <param name="item">The texture to be deleted</param>
|
||||||
protected override void Delete(Texture item)
|
protected override void Delete(Texture item)
|
||||||
{
|
{
|
||||||
if (item != null)
|
item?.DecrementReferenceCount(this);
|
||||||
{
|
|
||||||
item.DecrementReferenceCount(this);
|
|
||||||
RemoveAliasList(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
|
@ -509,7 +509,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (binding.IsImage)
|
if (binding.IsImage)
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture);
|
_context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -873,11 +873,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
|
Format format,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
|
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, format, isImage));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -896,11 +897,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index,
|
||||||
|
Format format)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
|
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index, format));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -919,11 +921,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index,
|
||||||
|
Format format)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
|
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index, format));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -34,26 +34,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The image format for the binding.
|
||||||
|
/// </summary>
|
||||||
|
public Format Format { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new buffer texture binding.
|
/// Create a new buffer texture binding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="array">Array</param>
|
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="index">Index of the binding on the array</param>
|
/// <param name="index">Index of the binding on the array</param>
|
||||||
|
/// <param name="format">Binding format</param>
|
||||||
public BufferTextureArrayBinding(
|
public BufferTextureArrayBinding(
|
||||||
T array,
|
T array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index,
|
||||||
|
Format format)
|
||||||
{
|
{
|
||||||
Array = array;
|
Array = array;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
Index = index;
|
Index = index;
|
||||||
|
Format = format;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureBindingInfo BindingInfo { get; }
|
public TextureBindingInfo BindingInfo { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The image format for the binding.
|
||||||
|
/// </summary>
|
||||||
|
public Format Format { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the binding is for an image or a sampler.
|
/// Whether the binding is for an image or a sampler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -42,18 +47,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
|
/// <param name="format">Binding format</param>
|
||||||
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
||||||
public BufferTextureBinding(
|
public BufferTextureBinding(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
|
Format format,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
|
Format = format;
|
||||||
IsImage = isImage;
|
IsImage = isImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@ -65,7 +64,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||||
Physical.TextureCache.Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -86,11 +86,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
ImageBindings[i] = stage.Info.Images.Select(descriptor =>
|
ImageBindings[i] = stage.Info.Images.Select(descriptor =>
|
||||||
{
|
{
|
||||||
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
Target target = ShaderTexture.GetTarget(descriptor.Type);
|
||||||
FormatInfo formatInfo = ShaderTexture.GetFormatInfo(descriptor.Format);
|
Format format = ShaderTexture.GetFormat(descriptor.Format);
|
||||||
|
|
||||||
var result = new TextureBindingInfo(
|
var result = new TextureBindingInfo(
|
||||||
target,
|
target,
|
||||||
formatInfo,
|
format,
|
||||||
descriptor.Set,
|
descriptor.Set,
|
||||||
descriptor.Binding,
|
descriptor.Binding,
|
||||||
descriptor.ArrayLength,
|
descriptor.ArrayLength,
|
||||||
|
@ -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 = 7353;
|
private const uint CodeGenVersion = 7131;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -743,7 +743,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
constantBufferUsePerStageMask &= ~(1 << index);
|
constantBufferUsePerStageMask &= ~(1 << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkTextures && _allTextures.Length > 0)
|
if (checkTextures)
|
||||||
{
|
{
|
||||||
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
bool isLinear,
|
bool isLinear,
|
||||||
int gobBlocksInY,
|
int gobBlocksInY,
|
||||||
Format format,
|
Format format,
|
||||||
byte bytesPerPixel,
|
int bytesPerPixel,
|
||||||
ImageCrop crop,
|
ImageCrop crop,
|
||||||
Action<GpuContext, object> acquireCallback,
|
Action<GpuContext, object> acquireCallback,
|
||||||
Action<object> releaseCallback,
|
Action<object> releaseCallback,
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Ryujinx.Graphics.OpenGL.Image;
|
|
||||||
using System;
|
|
||||||
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
|
||||||
{
|
|
||||||
internal class AreaScalingFilter : IScalingFilter
|
|
||||||
{
|
|
||||||
private readonly OpenGLRenderer _renderer;
|
|
||||||
private int _inputUniform;
|
|
||||||
private int _outputUniform;
|
|
||||||
private int _srcX0Uniform;
|
|
||||||
private int _srcX1Uniform;
|
|
||||||
private int _srcY0Uniform;
|
|
||||||
private int _scalingShaderProgram;
|
|
||||||
private int _srcY1Uniform;
|
|
||||||
private int _dstX0Uniform;
|
|
||||||
private int _dstX1Uniform;
|
|
||||||
private int _dstY0Uniform;
|
|
||||||
private int _dstY1Uniform;
|
|
||||||
|
|
||||||
public float Level { get; set; }
|
|
||||||
|
|
||||||
public AreaScalingFilter(OpenGLRenderer renderer)
|
|
||||||
{
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
_renderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_scalingShaderProgram != 0)
|
|
||||||
{
|
|
||||||
GL.DeleteProgram(_scalingShaderProgram);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
|
||||||
{
|
|
||||||
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/area_scaling.glsl");
|
|
||||||
|
|
||||||
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
|
|
||||||
|
|
||||||
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
|
|
||||||
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
|
|
||||||
|
|
||||||
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
|
|
||||||
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
|
|
||||||
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
|
|
||||||
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
|
|
||||||
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
|
|
||||||
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
|
|
||||||
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
|
|
||||||
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Run(
|
|
||||||
TextureView view,
|
|
||||||
TextureView destinationTexture,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
Extents2D source,
|
|
||||||
Extents2D destination)
|
|
||||||
{
|
|
||||||
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
|
||||||
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
|
||||||
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
|
||||||
|
|
||||||
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
|
||||||
|
|
||||||
int threadGroupWorkRegionDim = 16;
|
|
||||||
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
||||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
||||||
|
|
||||||
// Scaling pass
|
|
||||||
GL.UseProgram(_scalingShaderProgram);
|
|
||||||
view.Bind(0);
|
|
||||||
GL.Uniform1(_inputUniform, 0);
|
|
||||||
GL.Uniform1(_outputUniform, 0);
|
|
||||||
GL.Uniform1(_srcX0Uniform, (float)source.X1);
|
|
||||||
GL.Uniform1(_srcX1Uniform, (float)source.X2);
|
|
||||||
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
|
|
||||||
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
|
|
||||||
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
|
|
||||||
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
|
|
||||||
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
|
|
||||||
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
|
|
||||||
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
|
||||||
|
|
||||||
GL.UseProgram(previousProgram);
|
|
||||||
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
|
||||||
|
|
||||||
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
|
||||||
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
|
||||||
|
|
||||||
GL.ActiveTexture((TextureUnit)previousUnit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
|||||||
private int _srcY0Uniform;
|
private int _srcY0Uniform;
|
||||||
private int _scalingShaderProgram;
|
private int _scalingShaderProgram;
|
||||||
private int _sharpeningShaderProgram;
|
private int _sharpeningShaderProgram;
|
||||||
private float _sharpeningLevel = 1;
|
private float _scale = 1;
|
||||||
private int _srcY1Uniform;
|
private int _srcY1Uniform;
|
||||||
private int _dstX0Uniform;
|
private int _dstX0Uniform;
|
||||||
private int _dstX1Uniform;
|
private int _dstX1Uniform;
|
||||||
@ -30,10 +30,10 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
|||||||
|
|
||||||
public float Level
|
public float Level
|
||||||
{
|
{
|
||||||
get => _sharpeningLevel;
|
get => _scale;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_sharpeningLevel = MathF.Max(0.01f, value);
|
_scale = MathF.Max(0.01f, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Effects
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
{
|
{
|
||||||
@ -7,7 +6,18 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
|||||||
{
|
{
|
||||||
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
||||||
{
|
{
|
||||||
return CompileProgram(new string[] { shaderCode }, shaderType);
|
var shader = GL.CreateShader(shaderType);
|
||||||
|
GL.ShaderSource(shader, shaderCode);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
var program = GL.CreateProgram();
|
||||||
|
GL.AttachShader(program, shader);
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
|
||||||
|
GL.DetachShader(program, shader);
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
|
||||||
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
||||||
@ -16,15 +26,6 @@ namespace Ryujinx.Graphics.OpenGL.Effects
|
|||||||
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
||||||
GL.CompileShader(shader);
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
GL.GetShader(shader, ShaderParameter.CompileStatus, out int isCompiled);
|
|
||||||
if (isCompiled == 0)
|
|
||||||
{
|
|
||||||
string log = GL.GetShaderInfoLog(shader);
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Failed to compile effect shader:\n\n{log}\n");
|
|
||||||
GL.DeleteShader(shader);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var program = GL.CreateProgram();
|
var program = GL.CreateProgram();
|
||||||
GL.AttachShader(program, shader);
|
GL.AttachShader(program, shader);
|
||||||
GL.LinkProgram(program);
|
GL.LinkProgram(program);
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
#version 430 core
|
|
||||||
precision mediump float;
|
|
||||||
layout (local_size_x = 16, local_size_y = 16) in;
|
|
||||||
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
|
||||||
layout( location=1 ) uniform sampler2D Source;
|
|
||||||
layout( location=2 ) uniform float srcX0;
|
|
||||||
layout( location=3 ) uniform float srcX1;
|
|
||||||
layout( location=4 ) uniform float srcY0;
|
|
||||||
layout( location=5 ) uniform float srcY1;
|
|
||||||
layout( location=6 ) uniform float dstX0;
|
|
||||||
layout( location=7 ) uniform float dstX1;
|
|
||||||
layout( location=8 ) uniform float dstY0;
|
|
||||||
layout( location=9 ) uniform float dstY1;
|
|
||||||
|
|
||||||
/***** Area Sampling *****/
|
|
||||||
|
|
||||||
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
|
||||||
// Effectively a more accurate sharp bilinear filter when upscaling,
|
|
||||||
// that also works as a mathematically perfect downscale filter.
|
|
||||||
// https://entropymine.com/imageworsener/pixelmixing/
|
|
||||||
// https://github.com/obsproject/obs-studio/pull/1715
|
|
||||||
// https://legacy.imagemagick.org/Usage/filter/
|
|
||||||
vec4 AreaSampling(vec2 xy)
|
|
||||||
{
|
|
||||||
// Determine the sizes of the source and target images.
|
|
||||||
vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
|
|
||||||
vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
|
|
||||||
vec2 inverted_target_size = vec2(1.0) / target_size;
|
|
||||||
|
|
||||||
// Compute the top-left and bottom-right corners of the target pixel box.
|
|
||||||
vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
|
|
||||||
vec2 t_end = t_beg + vec2(1.0, 1.0);
|
|
||||||
|
|
||||||
// Convert the target pixel box to source pixel box.
|
|
||||||
vec2 beg = t_beg * inverted_target_size * source_size;
|
|
||||||
vec2 end = t_end * inverted_target_size * source_size;
|
|
||||||
|
|
||||||
// Compute the top-left and bottom-right corners of the pixel box.
|
|
||||||
ivec2 f_beg = ivec2(beg);
|
|
||||||
ivec2 f_end = ivec2(end);
|
|
||||||
|
|
||||||
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
|
||||||
float area_w = 1.0 - fract(beg.x);
|
|
||||||
float area_n = 1.0 - fract(beg.y);
|
|
||||||
float area_e = fract(end.x);
|
|
||||||
float area_s = fract(end.y);
|
|
||||||
|
|
||||||
// Compute the areas of the corner pixels in the pixel box.
|
|
||||||
float area_nw = area_n * area_w;
|
|
||||||
float area_ne = area_n * area_e;
|
|
||||||
float area_sw = area_s * area_w;
|
|
||||||
float area_se = area_s * area_e;
|
|
||||||
|
|
||||||
// Initialize the color accumulator.
|
|
||||||
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
// Accumulate corner pixels.
|
|
||||||
avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
|
|
||||||
avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
|
|
||||||
avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
|
|
||||||
avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
|
|
||||||
|
|
||||||
// Determine the size of the pixel box.
|
|
||||||
int x_range = int(f_end.x - f_beg.x - 0.5);
|
|
||||||
int y_range = int(f_end.y - f_beg.y - 0.5);
|
|
||||||
|
|
||||||
// Accumulate top and bottom edge pixels.
|
|
||||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
|
||||||
{
|
|
||||||
avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
|
|
||||||
avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate left and right edge pixels and all the pixels in between.
|
|
||||||
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
|
|
||||||
{
|
|
||||||
avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
|
|
||||||
avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
|
|
||||||
|
|
||||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
|
||||||
{
|
|
||||||
avg_color += texelFetch(Source, ivec2(x, y), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the area of the pixel box that was sampled.
|
|
||||||
float area_corners = area_nw + area_ne + area_sw + area_se;
|
|
||||||
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
|
||||||
float area_center = float(x_range) * float(y_range);
|
|
||||||
|
|
||||||
// Return the normalized average color.
|
|
||||||
return avg_color / (area_corners + area_edges + area_center);
|
|
||||||
}
|
|
||||||
|
|
||||||
float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
|
|
||||||
vec2 s = step(bLeft, v) - step(tRight, v);
|
|
||||||
return s.x * s.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 translateDest(vec2 pos) {
|
|
||||||
vec2 translatedPos = vec2(pos.x, pos.y);
|
|
||||||
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
|
||||||
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1 : translatedPos.y;
|
|
||||||
return translatedPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
|
|
||||||
vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
|
|
||||||
ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
|
||||||
if (insideBox(loc, bLeft, tRight) == 0) {
|
|
||||||
imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 outColor = AreaSampling(loc);
|
|
||||||
imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
|
|
||||||
}
|
|
@ -85,4 +85,4 @@ void main() {
|
|||||||
CurrFilter(gxy);
|
CurrFilter(gxy);
|
||||||
gxy.x -= 8u;
|
gxy.x -= 8u;
|
||||||
CurrFilter(gxy);
|
CurrFilter(gxy);
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
@ -18,6 +19,14 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
_images = new TextureRef[size];
|
_images = new TextureRef[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetFormats(int index, GAL.Format[] imageFormats)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < imageFormats.Length; i++)
|
||||||
|
{
|
||||||
|
_images[index + i].Format = imageFormats[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < images.Length; i++)
|
for (int i = 0; i < images.Length; i++)
|
||||||
@ -27,7 +36,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
if (image is TextureBase imageBase)
|
if (image is TextureBase imageBase)
|
||||||
{
|
{
|
||||||
_images[index + i].Handle = imageBase.Handle;
|
_images[index + i].Handle = imageBase.Handle;
|
||||||
_images[index + i].Format = imageBase.Format;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
{
|
{
|
||||||
@ -55,9 +55,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
var dataSpan = data.Span;
|
var dataSpan = data.Memory.Span;
|
||||||
|
|
||||||
Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
|
Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
|
||||||
|
|
||||||
@ -65,13 +65,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Image
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
@ -448,13 +448,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
var dataSpan = data.Span;
|
var dataSpan = data.Memory.Span;
|
||||||
fixed (byte* ptr = dataSpan)
|
fixed (byte* ptr = dataSpan)
|
||||||
{
|
{
|
||||||
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
ReadFrom((IntPtr)ptr, dataSpan.Length);
|
||||||
@ -463,13 +463,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data.Span)
|
fixed (byte* ptr = data.Memory.Span)
|
||||||
{
|
{
|
||||||
int width = Math.Max(Info.Width >> level, 1);
|
int width = Math.Max(Info.Width >> level, 1);
|
||||||
int height = Math.Max(Info.Height >> level, 1);
|
int height = Math.Max(Info.Height >> level, 1);
|
||||||
@ -480,7 +480,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
using (data = EnsureDataFormat(data))
|
using (data = EnsureDataFormat(data))
|
||||||
{
|
{
|
||||||
@ -489,7 +489,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data.Span)
|
fixed (byte* ptr = data.Memory.Span)
|
||||||
{
|
{
|
||||||
ReadFrom2D(
|
ReadFrom2D(
|
||||||
(IntPtr)ptr,
|
(IntPtr)ptr,
|
||||||
@ -522,13 +522,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
|
ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MemoryOwner<byte> EnsureDataFormat(MemoryOwner<byte> data)
|
private IMemoryOwner<byte> EnsureDataFormat(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
if (Format == Format.S8UintD24Unorm)
|
if (Format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
using (data)
|
using (data)
|
||||||
{
|
{
|
||||||
return FormatConverter.ConvertS8D24ToD24S8(data.Span);
|
return FormatConverter.ConvertS8D24ToD24S8(data.Memory.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,8 +202,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
||||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
||||||
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
|
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
|
||||||
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan.
|
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
||||||
maximumGpuMemory: 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||||
|
@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
|
private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
|
||||||
|
|
||||||
private readonly TextureBase[] _images;
|
private readonly (TextureBase, Format)[] _images;
|
||||||
private TextureBase _unit0Texture;
|
private TextureBase _unit0Texture;
|
||||||
private Sampler _unit0Sampler;
|
private Sampler _unit0Sampler;
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_fragmentOutputMap = uint.MaxValue;
|
_fragmentOutputMap = uint.MaxValue;
|
||||||
_componentMasks = uint.MaxValue;
|
_componentMasks = uint.MaxValue;
|
||||||
|
|
||||||
_images = new TextureBase[SavedImages];
|
_images = new (TextureBase, Format)[SavedImages];
|
||||||
|
|
||||||
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
|
_tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
|
||||||
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
|
_tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
|
||||||
@ -935,11 +935,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
SetFrontFace(_frontFace = frontFace.Convert());
|
SetFrontFace(_frontFace = frontFace.Convert());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture texture)
|
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||||
{
|
{
|
||||||
if ((uint)binding < SavedImages)
|
if ((uint)binding < SavedImages)
|
||||||
{
|
{
|
||||||
_images[binding] = texture as TextureBase;
|
_images[binding] = (texture as TextureBase, imageFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
@ -950,7 +950,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
TextureBase texBase = (TextureBase)texture;
|
TextureBase texBase = (TextureBase)texture;
|
||||||
|
|
||||||
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
|
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
||||||
|
|
||||||
if (format != 0)
|
if (format != 0)
|
||||||
{
|
{
|
||||||
@ -1622,11 +1622,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < SavedImages; i++)
|
for (int i = 0; i < SavedImages; i++)
|
||||||
{
|
{
|
||||||
TextureBase texBase = _images[i];
|
(TextureBase texBase, Format imageFormat) = _images[i];
|
||||||
|
|
||||||
if (texBase != null)
|
if (texBase != null)
|
||||||
{
|
{
|
||||||
SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
|
SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
|
||||||
|
|
||||||
if (format != 0)
|
if (format != 0)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\area_scaling.glsl" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -373,16 +373,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_isLinear = false;
|
_isLinear = false;
|
||||||
_scalingFilter.Level = _scalingFilterLevel;
|
_scalingFilter.Level = _scalingFilterLevel;
|
||||||
|
|
||||||
RecreateUpscalingTexture();
|
|
||||||
break;
|
|
||||||
case ScalingFilter.Area:
|
|
||||||
if (_scalingFilter is not AreaScalingFilter)
|
|
||||||
{
|
|
||||||
_scalingFilter?.Dispose();
|
|
||||||
_scalingFilter = new AreaScalingFilter(_renderer);
|
|
||||||
}
|
|
||||||
_isLinear = false;
|
|
||||||
|
|
||||||
RecreateUpscalingTexture();
|
RecreateUpscalingTexture();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -222,14 +222,30 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AtomOp.Min:
|
case AtomOp.And:
|
||||||
if (type == AtomSize.S32)
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
{
|
{
|
||||||
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
res = context.AtomicAnd(storageKind, e0, e1, value);
|
||||||
}
|
}
|
||||||
else if (type == AtomSize.U32)
|
else
|
||||||
{
|
{
|
||||||
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AtomOp.Xor:
|
||||||
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicXor(storageKind, e0, e1, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AtomOp.Or:
|
||||||
|
if (type == AtomSize.S32 || type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicOr(storageKind, e0, e1, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -250,49 +266,20 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AtomOp.And:
|
case AtomOp.Min:
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
if (type == AtomSize.S32)
|
||||||
{
|
{
|
||||||
res = context.AtomicAnd(storageKind, e0, e1, value);
|
res = context.AtomicMinS32(storageKind, e0, e1, value);
|
||||||
|
}
|
||||||
|
else if (type == AtomSize.U32)
|
||||||
|
{
|
||||||
|
res = context.AtomicMinU32(storageKind, e0, e1, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AtomOp.Or:
|
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicOr(storageKind, e0, e1, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AtomOp.Xor:
|
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicXor(storageKind, e0, e1, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AtomOp.Exch:
|
|
||||||
if (type == AtomSize.S32 || type == AtomSize.U32)
|
|
||||||
{
|
|
||||||
res = context.AtomicSwap(storageKind, e0, e1, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid reduction type: {type}.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
context.TranslatorContext.GpuAccessor.Log($"Invalid atomic operation: {op}.");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -138,8 +138,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// Ensure that conditions met for that branch are also met for the current one.
|
// Ensure that conditions met for that branch are also met for the current one.
|
||||||
// Prefer the latest sources for the phi node.
|
// Prefer the latest sources for the phi node.
|
||||||
|
|
||||||
int undefCount = 0;
|
|
||||||
|
|
||||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
BasicBlock phiBlock = phiNode.GetBlock(i);
|
BasicBlock phiBlock = phiNode.GetBlock(i);
|
||||||
@ -161,26 +159,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (phiSource.Type == OperandType.Undefined)
|
|
||||||
{
|
|
||||||
undefCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all sources but one are undefined, we can assume that the one
|
|
||||||
// that is not undefined is the right one.
|
|
||||||
|
|
||||||
if (undefCount == phiNode.SourcesCount - 1)
|
|
||||||
{
|
|
||||||
for (int i = phiNode.SourcesCount - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
Operand phiSource = phiNode.GetSource(i);
|
|
||||||
|
|
||||||
if (phiSource.Type != OperandType.Undefined)
|
|
||||||
{
|
|
||||||
return phiSource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (stage == ShaderStage.Vertex)
|
if (stage == ShaderStage.Vertex)
|
||||||
{
|
{
|
||||||
InitializeVertexOutputs(context);
|
InitializePositionOutput(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
||||||
@ -236,20 +236,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeVertexOutputs(EmitterContext context)
|
private static void InitializePositionOutput(EmitterContext context)
|
||||||
{
|
{
|
||||||
for (int c = 0; c < 4; c++)
|
for (int c = 0; c < 4; c++)
|
||||||
{
|
{
|
||||||
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Program.ClipDistancesWritten != 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
||||||
|
@ -82,6 +82,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly ImageRef[] _imageRefs;
|
private readonly ImageRef[] _imageRefs;
|
||||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||||
private readonly TextureBuffer[] _bufferImageRefs;
|
private readonly TextureBuffer[] _bufferImageRefs;
|
||||||
|
private readonly Format[] _bufferImageFormats;
|
||||||
|
|
||||||
private ArrayRef<TextureArray>[] _textureArrayRefs;
|
private ArrayRef<TextureArray>[] _textureArrayRefs;
|
||||||
private ArrayRef<ImageArray>[] _imageArrayRefs;
|
private ArrayRef<ImageArray>[] _imageArrayRefs;
|
||||||
@ -140,6 +141,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
||||||
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
|
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
|
||||||
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
|
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
|
||||||
|
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
|
||||||
|
|
||||||
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
|
_textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>();
|
||||||
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
|
_imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>();
|
||||||
@ -389,11 +391,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_dirty = DirtyFlags.All;
|
_dirty = DirtyFlags.All;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture image)
|
public void SetImage(
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
ShaderStage stage,
|
||||||
|
int binding,
|
||||||
|
ITexture image,
|
||||||
|
Format imageFormat)
|
||||||
{
|
{
|
||||||
if (image is TextureBuffer imageBuffer)
|
if (image is TextureBuffer imageBuffer)
|
||||||
{
|
{
|
||||||
_bufferImageRefs[binding] = imageBuffer;
|
_bufferImageRefs[binding] = imageBuffer;
|
||||||
|
_bufferImageFormats[binding] = imageFormat;
|
||||||
}
|
}
|
||||||
else if (image is TextureView view)
|
else if (image is TextureView view)
|
||||||
{
|
{
|
||||||
@ -402,12 +410,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||||
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
|
|
||||||
iRef = new(stage, view, view.GetIdentityImageView());
|
iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_imageRefs[binding] = default;
|
_imageRefs[binding] = default;
|
||||||
_bufferImageRefs[binding] = null;
|
_bufferImageRefs[binding] = null;
|
||||||
|
_bufferImageFormats[binding] = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalDirty(DirtyFlags.Image);
|
SignalDirty(DirtyFlags.Image);
|
||||||
@ -914,7 +923,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default;
|
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
tu.Push<BufferView>(bufferImages[..count]);
|
tu.Push<BufferView>(bufferImages[..count]);
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using Silk.NET.Vulkan;
|
|
||||||
using System;
|
|
||||||
using Extent2D = Ryujinx.Graphics.GAL.Extents2D;
|
|
||||||
using Format = Silk.NET.Vulkan.Format;
|
|
||||||
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan.Effects
|
|
||||||
{
|
|
||||||
internal class AreaScalingFilter : IScalingFilter
|
|
||||||
{
|
|
||||||
private readonly VulkanRenderer _renderer;
|
|
||||||
private PipelineHelperShader _pipeline;
|
|
||||||
private ISampler _sampler;
|
|
||||||
private ShaderCollection _scalingProgram;
|
|
||||||
private Device _device;
|
|
||||||
|
|
||||||
public float Level { get; set; }
|
|
||||||
|
|
||||||
public AreaScalingFilter(VulkanRenderer renderer, Device device)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
_renderer = renderer;
|
|
||||||
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_pipeline.Dispose();
|
|
||||||
_scalingProgram.Dispose();
|
|
||||||
_sampler.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
_pipeline = new PipelineHelperShader(_renderer, _device);
|
|
||||||
|
|
||||||
_pipeline.Initialize();
|
|
||||||
|
|
||||||
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/AreaScaling.spv");
|
|
||||||
|
|
||||||
var scalingResourceLayout = new ResourceLayoutBuilder()
|
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
|
|
||||||
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
|
|
||||||
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
|
|
||||||
|
|
||||||
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
|
||||||
|
|
||||||
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
|
||||||
{
|
|
||||||
new ShaderSource(scalingShader, ShaderStage.Compute, TargetLanguage.Spirv),
|
|
||||||
}, scalingResourceLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Run(
|
|
||||||
TextureView view,
|
|
||||||
CommandBufferScoped cbs,
|
|
||||||
Auto<DisposableImageView> destinationTexture,
|
|
||||||
Format format,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
Extent2D source,
|
|
||||||
Extent2D destination)
|
|
||||||
{
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
|
||||||
_pipeline.SetProgram(_scalingProgram);
|
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler);
|
|
||||||
|
|
||||||
ReadOnlySpan<float> dimensionsBuffer = stackalloc float[]
|
|
||||||
{
|
|
||||||
source.X1,
|
|
||||||
source.X2,
|
|
||||||
source.Y1,
|
|
||||||
source.Y2,
|
|
||||||
destination.X1,
|
|
||||||
destination.X2,
|
|
||||||
destination.Y1,
|
|
||||||
destination.Y2,
|
|
||||||
};
|
|
||||||
|
|
||||||
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
|
||||||
using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize);
|
|
||||||
buffer.Holder.SetDataUnchecked(buffer.Offset, dimensionsBuffer);
|
|
||||||
|
|
||||||
int threadGroupWorkRegionDim = 16;
|
|
||||||
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
||||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
|
||||||
_pipeline.SetImage(0, destinationTexture);
|
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
|
||||||
_pipeline.ComputeBarrier();
|
|
||||||
|
|
||||||
_pipeline.Finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _texture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
// Scaling
|
|
||||||
|
|
||||||
#version 430 core
|
|
||||||
layout (local_size_x = 16, local_size_y = 16) in;
|
|
||||||
layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput;
|
|
||||||
layout( binding = 1, set = 2) uniform sampler2D Source;
|
|
||||||
layout( binding = 2 ) uniform dimensions{
|
|
||||||
float srcX0;
|
|
||||||
float srcX1;
|
|
||||||
float srcY0;
|
|
||||||
float srcY1;
|
|
||||||
float dstX0;
|
|
||||||
float dstX1;
|
|
||||||
float dstY0;
|
|
||||||
float dstY1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/***** Area Sampling *****/
|
|
||||||
|
|
||||||
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
|
||||||
// Effectively a more accurate sharp bilinear filter when upscaling,
|
|
||||||
// that also works as a mathematically perfect downscale filter.
|
|
||||||
// https://entropymine.com/imageworsener/pixelmixing/
|
|
||||||
// https://github.com/obsproject/obs-studio/pull/1715
|
|
||||||
// https://legacy.imagemagick.org/Usage/filter/
|
|
||||||
vec4 AreaSampling(vec2 xy)
|
|
||||||
{
|
|
||||||
// Determine the sizes of the source and target images.
|
|
||||||
vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
|
|
||||||
vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
|
|
||||||
vec2 inverted_target_size = vec2(1.0) / target_size;
|
|
||||||
|
|
||||||
// Compute the top-left and bottom-right corners of the target pixel box.
|
|
||||||
vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
|
|
||||||
vec2 t_end = t_beg + vec2(1.0, 1.0);
|
|
||||||
|
|
||||||
// Convert the target pixel box to source pixel box.
|
|
||||||
vec2 beg = t_beg * inverted_target_size * source_size;
|
|
||||||
vec2 end = t_end * inverted_target_size * source_size;
|
|
||||||
|
|
||||||
// Compute the top-left and bottom-right corners of the pixel box.
|
|
||||||
ivec2 f_beg = ivec2(beg);
|
|
||||||
ivec2 f_end = ivec2(end);
|
|
||||||
|
|
||||||
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
|
||||||
float area_w = 1.0 - fract(beg.x);
|
|
||||||
float area_n = 1.0 - fract(beg.y);
|
|
||||||
float area_e = fract(end.x);
|
|
||||||
float area_s = fract(end.y);
|
|
||||||
|
|
||||||
// Compute the areas of the corner pixels in the pixel box.
|
|
||||||
float area_nw = area_n * area_w;
|
|
||||||
float area_ne = area_n * area_e;
|
|
||||||
float area_sw = area_s * area_w;
|
|
||||||
float area_se = area_s * area_e;
|
|
||||||
|
|
||||||
// Initialize the color accumulator.
|
|
||||||
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
// Accumulate corner pixels.
|
|
||||||
avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
|
|
||||||
avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
|
|
||||||
avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
|
|
||||||
avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
|
|
||||||
|
|
||||||
// Determine the size of the pixel box.
|
|
||||||
int x_range = int(f_end.x - f_beg.x - 0.5);
|
|
||||||
int y_range = int(f_end.y - f_beg.y - 0.5);
|
|
||||||
|
|
||||||
// Accumulate top and bottom edge pixels.
|
|
||||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
|
||||||
{
|
|
||||||
avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
|
|
||||||
avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate left and right edge pixels and all the pixels in between.
|
|
||||||
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
|
|
||||||
{
|
|
||||||
avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
|
|
||||||
avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
|
|
||||||
|
|
||||||
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
|
|
||||||
{
|
|
||||||
avg_color += texelFetch(Source, ivec2(x, y), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the area of the pixel box that was sampled.
|
|
||||||
float area_corners = area_nw + area_ne + area_sw + area_se;
|
|
||||||
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
|
||||||
float area_center = float(x_range) * float(y_range);
|
|
||||||
|
|
||||||
// Return the normalized average color.
|
|
||||||
return avg_color / (area_corners + area_edges + area_center);
|
|
||||||
}
|
|
||||||
|
|
||||||
float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
|
|
||||||
vec2 s = step(bLeft, v) - step(tRight, v);
|
|
||||||
return s.x * s.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 translateDest(vec2 pos) {
|
|
||||||
vec2 translatedPos = vec2(pos.x, pos.y);
|
|
||||||
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
|
||||||
translatedPos.y = dstY0 < dstY1 ? dstY1 + dstY0 - translatedPos.y - 1 : translatedPos.y;
|
|
||||||
return translatedPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
|
|
||||||
vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
|
|
||||||
ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
|
|
||||||
if (insideBox(loc, bLeft, tRight) == 0) {
|
|
||||||
imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 outColor = AreaSampling(loc);
|
|
||||||
imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
|
|
||||||
}
|
|
Binary file not shown.
@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
|
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_pipeline.Specialize(_specConstants);
|
_pipeline.Specialize(_specConstants);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format)));
|
_pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
|
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(dstFormat));
|
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat);
|
||||||
|
|
||||||
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
|
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
|
||||||
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
|
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
|
||||||
@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||||
|
|
||||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format));
|
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
|
||||||
|
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
public TextureStorage Storage;
|
public TextureStorage Storage;
|
||||||
public TextureView View;
|
public TextureView View;
|
||||||
|
public GAL.Format ImageFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly TextureRef[] _textureRefs;
|
private readonly TextureRef[] _textureRefs;
|
||||||
@ -51,6 +52,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_isBuffer = isBuffer;
|
_isBuffer = isBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetFormats(int index, GAL.Format[] imageFormats)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < imageFormats.Length; i++)
|
||||||
|
{
|
||||||
|
_textureRefs[index + i].ImageFormat = imageFormats[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetImages(int index, ITexture[] images)
|
public void SetImages(int index, ITexture[] images)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < images.Length; i++)
|
for (int i = 0; i < images.Length; i++)
|
||||||
@ -131,7 +142,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ref var texture = ref textures[i];
|
ref var texture = ref textures[i];
|
||||||
ref var refs = ref _textureRefs[i];
|
ref var refs = ref _textureRefs[i];
|
||||||
|
|
||||||
if (i > 0 && _textureRefs[i - 1].View == refs.View)
|
if (i > 0 && _textureRefs[i - 1].View == refs.View && _textureRefs[i - 1].ImageFormat == refs.ImageFormat)
|
||||||
{
|
{
|
||||||
texture = textures[i - 1];
|
texture = textures[i - 1];
|
||||||
|
|
||||||
@ -139,7 +150,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
texture.ImageLayout = ImageLayout.General;
|
texture.ImageLayout = ImageLayout.General;
|
||||||
texture.ImageView = refs.View?.GetIdentityImageView().Get(cbs).Value ?? default;
|
texture.ImageView = refs.View?.GetView(refs.ImageFormat).GetIdentityImageView().Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
{
|
{
|
||||||
@ -156,7 +167,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
for (int i = 0; i < bufferTextures.Length; i++)
|
for (int i = 0; i < bufferTextures.Length; i++)
|
||||||
{
|
{
|
||||||
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, true) ?? default;
|
bufferTextures[i] = _bufferTextureRefs[i]?.GetBufferView(cbs, _textureRefs[i].ImageFormat, true) ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bufferTextures;
|
return bufferTextures;
|
||||||
|
@ -636,9 +636,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var oldStencilTestEnable = _newState.StencilTestEnable;
|
var oldStencilTestEnable = _newState.StencilTestEnable;
|
||||||
var oldDepthTestEnable = _newState.DepthTestEnable;
|
var oldDepthTestEnable = _newState.DepthTestEnable;
|
||||||
var oldDepthWriteEnable = _newState.DepthWriteEnable;
|
var oldDepthWriteEnable = _newState.DepthWriteEnable;
|
||||||
|
var oldTopology = _newState.Topology;
|
||||||
var oldViewports = DynamicState.Viewports;
|
var oldViewports = DynamicState.Viewports;
|
||||||
var oldViewportsCount = _newState.ViewportsCount;
|
var oldViewportsCount = _newState.ViewportsCount;
|
||||||
var oldTopology = _topology;
|
|
||||||
|
|
||||||
_newState.CullMode = CullModeFlags.None;
|
_newState.CullMode = CullModeFlags.None;
|
||||||
_newState.StencilTestEnable = false;
|
_newState.StencilTestEnable = false;
|
||||||
@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_newState.StencilTestEnable = oldStencilTestEnable;
|
_newState.StencilTestEnable = oldStencilTestEnable;
|
||||||
_newState.DepthTestEnable = oldDepthTestEnable;
|
_newState.DepthTestEnable = oldDepthTestEnable;
|
||||||
_newState.DepthWriteEnable = oldDepthWriteEnable;
|
_newState.DepthWriteEnable = oldDepthWriteEnable;
|
||||||
SetPrimitiveTopology(oldTopology);
|
_newState.Topology = oldTopology;
|
||||||
|
|
||||||
DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
|
DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
|
||||||
|
|
||||||
@ -836,9 +836,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(ShaderStage stage, int binding, ITexture image)
|
public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat)
|
||||||
{
|
{
|
||||||
_descriptorSetUpdater.SetImage(Cbs, stage, binding, image);
|
_descriptorSetUpdater.SetImage(Cbs, stage, binding, image, imageFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImage(int binding, Auto<DisposableImageView> image)
|
public void SetImage(int binding, Auto<DisposableImageView> image)
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
|
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
|
||||||
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
|
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\AreaScaling.spv" />
|
|
||||||
<EmbeddedResource Include="Effects\Shaders\FsrScaling.spv" />
|
<EmbeddedResource Include="Effects\Shaders\FsrScaling.spv" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\FsrSharpening.spv" />
|
<EmbeddedResource Include="Effects\Shaders\FsrSharpening.spv" />
|
||||||
<EmbeddedResource Include="Effects\Shaders\Fxaa.spv" />
|
<EmbeddedResource Include="Effects\Shaders\Fxaa.spv" />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Format = Ryujinx.Graphics.GAL.Format;
|
using Format = Ryujinx.Graphics.GAL.Format;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private int _offset;
|
private int _offset;
|
||||||
private int _size;
|
private int _size;
|
||||||
private Auto<DisposableBufferView> _bufferView;
|
private Auto<DisposableBufferView> _bufferView;
|
||||||
|
private Dictionary<Format, Auto<DisposableBufferView>> _selfManagedViews;
|
||||||
|
|
||||||
private int _bufferCount;
|
private int _bufferCount;
|
||||||
|
|
||||||
@ -79,25 +80,35 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private void ReleaseImpl()
|
private void ReleaseImpl()
|
||||||
{
|
{
|
||||||
|
if (_selfManagedViews != null)
|
||||||
|
{
|
||||||
|
foreach (var bufferView in _selfManagedViews.Values)
|
||||||
|
{
|
||||||
|
bufferView.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_selfManagedViews = null;
|
||||||
|
}
|
||||||
|
|
||||||
_bufferView?.Dispose();
|
_bufferView?.Dispose();
|
||||||
_bufferView = null;
|
_bufferView = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
_gd.SetBufferData(_bufferHandle, _offset, data.Span);
|
_gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span);
|
||||||
data.Dispose();
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
@ -126,5 +137,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
return _bufferView?.Get(cbs, _offset, _size, write).Value ?? default;
|
return _bufferView?.Get(cbs, _offset, _size, write).Value ?? default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BufferView GetBufferView(CommandBufferScoped cbs, Format format, bool write)
|
||||||
|
{
|
||||||
|
var vkFormat = FormatTable.GetFormat(format);
|
||||||
|
if (vkFormat == VkFormat)
|
||||||
|
{
|
||||||
|
return GetBufferView(cbs, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selfManagedViews != null && _selfManagedViews.TryGetValue(format, out var bufferView))
|
||||||
|
{
|
||||||
|
return bufferView.Get(cbs, _offset, _size, write).Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferView = _gd.BufferManager.CreateView(_bufferHandle, vkFormat, _offset, _size, ReleaseImpl);
|
||||||
|
|
||||||
|
if (bufferView != null)
|
||||||
|
{
|
||||||
|
(_selfManagedViews ??= new Dictionary<Format, Auto<DisposableBufferView>>()).Add(format, bufferView);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferView?.Get(cbs, _offset, _size, write).Value ?? default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -746,23 +746,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data)
|
public void SetData(IMemoryOwner<byte> data)
|
||||||
{
|
{
|
||||||
SetData(data.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
SetData(data.Memory.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
|
||||||
data.Dispose();
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
SetData(data.Span, layer, level, 1, 1, singleSlice: true);
|
SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true);
|
||||||
data.Dispose();
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
|
||||||
{
|
{
|
||||||
SetData(data.Span, layer, level, 1, 1, singleSlice: true, region);
|
SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true, region);
|
||||||
data.Dispose();
|
data.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,26 +781,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
||||||
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
||||||
textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
|
textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
|
||||||
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0,
|
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
|
||||||
maximumGpuMemory: GetTotalGPUMemory());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong GetTotalGPUMemory()
|
|
||||||
{
|
|
||||||
ulong totalMemory = 0;
|
|
||||||
|
|
||||||
Api.GetPhysicalDeviceMemoryProperties(_physicalDevice.PhysicalDevice, out PhysicalDeviceMemoryProperties memoryProperties);
|
|
||||||
|
|
||||||
for (int i = 0; i < memoryProperties.MemoryHeapCount; i++)
|
|
||||||
{
|
|
||||||
var heap = memoryProperties.MemoryHeaps[i];
|
|
||||||
if ((heap.Flags & MemoryHeapFlags.DeviceLocalBit) == MemoryHeapFlags.DeviceLocalBit)
|
|
||||||
{
|
|
||||||
totalMemory += heap.Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalMemory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HardwareInfo GetHardwareInfo()
|
public HardwareInfo GetHardwareInfo()
|
||||||
@ -884,7 +865,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private void PrintGpuInformation()
|
private void PrintGpuInformation()
|
||||||
{
|
{
|
||||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||||
Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(GraphicsDebugLevel logLevel)
|
public void Initialize(GraphicsDebugLevel logLevel)
|
||||||
|
@ -568,13 +568,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_scalingFilter.Level = _scalingFilterLevel;
|
_scalingFilter.Level = _scalingFilterLevel;
|
||||||
break;
|
break;
|
||||||
case ScalingFilter.Area:
|
|
||||||
if (_scalingFilter is not AreaScalingFilter)
|
|
||||||
{
|
|
||||||
_scalingFilter?.Dispose();
|
|
||||||
_scalingFilter = new AreaScalingFilter(_gd, _device);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
{
|
{
|
||||||
private readonly MemoryOwner<byte> _rawDataOwner;
|
private readonly MemoryOwner<byte> _rawDataOwner;
|
||||||
|
|
||||||
private Span<byte> Raw => _rawDataOwner.Span;
|
private Span<byte> Raw => _rawDataOwner.Memory.Span;
|
||||||
|
|
||||||
private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(Raw)[0];
|
private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(Raw)[0];
|
||||||
|
|
||||||
|
@ -412,9 +412,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
|
Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
|
||||||
|
|
||||||
byte bytesPerPixel =
|
int bytesPerPixel =
|
||||||
format == Format.B5G6R5Unorm ||
|
format == Format.B5G6R5Unorm ||
|
||||||
format == Format.R4G4B4A4Unorm ? (byte)2 : (byte)4;
|
format == Format.R4G4B4A4Unorm ? 2 : 4;
|
||||||
|
|
||||||
int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2;
|
int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2;
|
||||||
|
|
||||||
|
@ -8,6 +8,5 @@ namespace Ryujinx.Horizon.Sdk.Audio
|
|||||||
|
|
||||||
public static Result DeviceNotFound => new(ModuleId, 1);
|
public static Result DeviceNotFound => new(ModuleId, 1);
|
||||||
public static Result UnsupportedRevision => new(ModuleId, 2);
|
public static Result UnsupportedRevision => new(ModuleId, 2);
|
||||||
public static Result NotImplemented => new(ModuleId, 513);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,48 +233,6 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CmifCommand(15)] // 17.0.0+
|
|
||||||
public Result AcquireAudioOutputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId)
|
|
||||||
{
|
|
||||||
eventHandle = 0;
|
|
||||||
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CmifCommand(16)] // 17.0.0+
|
|
||||||
public Result ReleaseAudioOutputDeviceNotification(ulong deviceId)
|
|
||||||
{
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CmifCommand(17)] // 17.0.0+
|
|
||||||
public Result AcquireAudioInputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId)
|
|
||||||
{
|
|
||||||
eventHandle = 0;
|
|
||||||
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CmifCommand(18)] // 17.0.0+
|
|
||||||
public Result ReleaseAudioInputDeviceNotification(ulong deviceId)
|
|
||||||
{
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CmifCommand(19)] // 18.0.0+
|
|
||||||
public Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CmifCommand(20)] // 18.0.0+
|
|
||||||
public Result IsAudioDeviceOutputVolumeAutoTuneEnabled(out bool enabled)
|
|
||||||
{
|
|
||||||
enabled = false;
|
|
||||||
|
|
||||||
return AudioResult.NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Ryujinx.Common.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Memory
|
namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
@ -7,7 +7,7 @@ namespace Ryujinx.Memory
|
|||||||
{
|
{
|
||||||
private readonly IWritableBlock _block;
|
private readonly IWritableBlock _block;
|
||||||
private readonly ulong _va;
|
private readonly ulong _va;
|
||||||
private readonly MemoryOwner<byte> _memoryOwner;
|
private readonly IMemoryOwner<byte> _memoryOwner;
|
||||||
private readonly bool _tracked;
|
private readonly bool _tracked;
|
||||||
|
|
||||||
private bool NeedsWriteback => _block != null;
|
private bool NeedsWriteback => _block != null;
|
||||||
@ -22,7 +22,7 @@ namespace Ryujinx.Memory
|
|||||||
Memory = memory;
|
Memory = memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WritableRegion(IWritableBlock block, ulong va, MemoryOwner<byte> memoryOwner, bool tracked = false)
|
public WritableRegion(IWritableBlock block, ulong va, IMemoryOwner<byte> memoryOwner, bool tracked = false)
|
||||||
: this(block, va, memoryOwner.Memory, tracked)
|
: this(block, va, memoryOwner.Memory, tracked)
|
||||||
{
|
{
|
||||||
_memoryOwner = memoryOwner;
|
_memoryOwner = memoryOwner;
|
||||||
|
@ -53,7 +53,6 @@ namespace Ryujinx.SDL2.Common
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx");
|
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||||
|
@ -55,7 +55,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -84,7 +83,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -113,7 +111,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -142,7 +139,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -171,7 +167,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -200,7 +195,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -229,7 +223,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -258,7 +251,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -287,7 +279,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -316,7 +307,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -345,7 +335,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
@ -374,36 +363,6 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
|||||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
|
||||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
|
||||||
Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestRevision13()
|
|
||||||
{
|
|
||||||
BehaviourContext behaviourContext = new();
|
|
||||||
|
|
||||||
behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision13);
|
|
||||||
|
|
||||||
Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed());
|
|
||||||
Assert.IsTrue(behaviourContext.IsSplitterSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsSplitterBugFixed());
|
|
||||||
Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
|
||||||
Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported());
|
|
||||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
|
||||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
|
||||||
Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
|
||||||
Assert.IsTrue(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
|
||||||
|
|
||||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||||
|
@ -758,11 +758,10 @@
|
|||||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
||||||
"GraphicsAALabel": "Anti-Aliasing:",
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
||||||
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
||||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||||
"GraphicsScalingFilterNearest": "Nearest",
|
"GraphicsScalingFilterNearest": "Nearest",
|
||||||
"GraphicsScalingFilterFsr": "FSR",
|
"GraphicsScalingFilterFsr": "FSR",
|
||||||
"GraphicsScalingFilterArea": "Area",
|
|
||||||
"GraphicsScalingFilterLevelLabel": "Level",
|
"GraphicsScalingFilterLevelLabel": "Level",
|
||||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
||||||
"SmaaLow": "SMAA Low",
|
"SmaaLow": "SMAA Low",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
|
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@ -173,9 +173,6 @@
|
|||||||
<ComboBoxItem>
|
<ComboBoxItem>
|
||||||
<TextBlock Text="{locale:Locale GraphicsScalingFilterFsr}" />
|
<TextBlock Text="{locale:Locale GraphicsScalingFilterFsr}" />
|
||||||
</ComboBoxItem>
|
</ComboBoxItem>
|
||||||
<ComboBoxItem>
|
|
||||||
<TextBlock Text="{locale:Locale GraphicsScalingFilterArea}" />
|
|
||||||
</ComboBoxItem>
|
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<controls:SliderScroll Value="{Binding ScalingFilterLevel}"
|
<controls:SliderScroll Value="{Binding ScalingFilterLevel}"
|
||||||
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user