Compare commits
No commits in common. "master" and "1.1.1388" 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -647,7 +647,7 @@ namespace Ryujinx.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value
|
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value
|
||||||
? HLE.MemoryConfiguration.MemoryConfiguration8GiB
|
? HLE.MemoryConfiguration.MemoryConfiguration6GiB
|
||||||
: HLE.MemoryConfiguration.MemoryConfiguration4GiB;
|
: HLE.MemoryConfiguration.MemoryConfiguration4GiB;
|
||||||
|
|
||||||
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
|
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
|
||||||
|
@ -28,8 +28,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
MemoryArrange.MemoryArrange4GiBSystemDev or
|
MemoryArrange.MemoryArrange4GiBSystemDev or
|
||||||
MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB,
|
MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB,
|
||||||
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
|
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
|
||||||
MemoryArrange.MemoryArrange6GiB => 4916 * MiB,
|
MemoryArrange.MemoryArrange6GiB or
|
||||||
MemoryArrange.MemoryArrange8GiB => 6964 * MiB,
|
MemoryArrange.MemoryArrange8GiB => 4916 * MiB,
|
||||||
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -42,8 +42,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB,
|
MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB,
|
||||||
MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB,
|
MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB,
|
||||||
MemoryArrange.MemoryArrange6GiB => 562 * MiB,
|
MemoryArrange.MemoryArrange6GiB => 562 * MiB,
|
||||||
MemoryArrange.MemoryArrange6GiBAppletDev => 2193 * MiB,
|
MemoryArrange.MemoryArrange6GiBAppletDev or
|
||||||
MemoryArrange.MemoryArrange8GiB => 562 * MiB,
|
MemoryArrange.MemoryArrange8GiB => 2193 * MiB,
|
||||||
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
// Hacks
|
// Hacks
|
||||||
|
|
||||||
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 8GiB.")]
|
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")]
|
||||||
public bool ExpandRAM { get; set; }
|
public bool ExpandRAM { get; set; }
|
||||||
|
|
||||||
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
|
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
|
||||||
|
@ -562,7 +562,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_userChannelPersistence,
|
_userChannelPersistence,
|
||||||
renderer,
|
renderer,
|
||||||
new SDL2HardwareDeviceDriver(),
|
new SDL2HardwareDeviceDriver(),
|
||||||
options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
|
||||||
window,
|
window,
|
||||||
options.SystemLanguage,
|
options.SystemLanguage,
|
||||||
options.SystemRegion,
|
options.SystemRegion,
|
||||||
|
@ -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());
|
||||||
|
@ -238,7 +238,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
public MemoryManagerMode MemoryManagerMode { get; set; }
|
public MemoryManagerMode MemoryManagerMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Expands the RAM amount on the emulated system from 4GiB to 8GiB
|
/// Expands the RAM amount on the emulated system from 4GiB to 6GiB
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ExpandRam { get; set; }
|
public bool ExpandRam { get; set; }
|
||||||
|
|
||||||
|
@ -845,7 +845,7 @@ namespace Ryujinx.Ava
|
|||||||
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
|
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
|
||||||
|
|
||||||
// Initialize Configuration.
|
// Initialize Configuration.
|
||||||
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB;
|
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB;
|
||||||
|
|
||||||
HLEConfiguration configuration = new(VirtualFileSystem,
|
HLEConfiguration configuration = new(VirtualFileSystem,
|
||||||
_viewModel.LibHacHorizonManager,
|
_viewModel.LibHacHorizonManager,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user