mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-29 18:37:11 +02:00
Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,115 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced blend manager.
|
||||
/// </summary>
|
||||
class AdvancedBlendManager
|
||||
{
|
||||
private const int InstructionRamSize = 128;
|
||||
private const int InstructionRamSizeMask = InstructionRamSize - 1;
|
||||
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
|
||||
private readonly uint[] _code;
|
||||
private int _ip;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the advanced blend manager.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state of the channel owning this manager</param>
|
||||
public AdvancedBlendManager(DeviceStateWithShadow<ThreedClassState> state)
|
||||
{
|
||||
_state = state;
|
||||
_code = new uint[InstructionRamSize];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the start offset of the blend microcode in memory.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void LoadBlendUcodeStart(int argument)
|
||||
{
|
||||
_ip = argument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes one word of blend microcode.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void LoadBlendUcodeInstruction(int argument)
|
||||
{
|
||||
_code[_ip++ & InstructionRamSizeMask] = (uint)argument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to identify the current advanced blend function being used,
|
||||
/// given the current state and microcode that was uploaded.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">Advanced blend descriptor</param>
|
||||
/// <returns>True if the function was found, false otherwise</returns>
|
||||
public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor)
|
||||
{
|
||||
Span<uint> currentCode = new Span<uint>(_code);
|
||||
byte codeLength = (byte)_state.State.BlendUcodeSize;
|
||||
|
||||
if (currentCode.Length > codeLength)
|
||||
{
|
||||
currentCode = currentCode.Slice(0, codeLength);
|
||||
}
|
||||
|
||||
Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(currentCode));
|
||||
|
||||
descriptor = default;
|
||||
|
||||
if (!AdvancedBlendPreGenTable.Entries.TryGetValue(hash, out var entry))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.Constants != null)
|
||||
{
|
||||
bool constantsMatch = true;
|
||||
|
||||
for (int i = 0; i < entry.Constants.Length; i++)
|
||||
{
|
||||
RgbFloat constant = entry.Constants[i];
|
||||
RgbHalf constant2 = _state.State.BlendUcodeConstants[i];
|
||||
|
||||
if ((Half)constant.R != constant2.UnpackR() ||
|
||||
(Half)constant.G != constant2.UnpackG() ||
|
||||
(Half)constant.B != constant2.UnpackB())
|
||||
{
|
||||
constantsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!constantsMatch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.Alpha.Enable != _state.State.BlendUcodeEnable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.Alpha.Enable == BlendUcodeEnable.EnableRGBA &&
|
||||
(entry.Alpha.AlphaOp != _state.State.BlendStateCommon.AlphaOp ||
|
||||
entry.Alpha.AlphaSrcFactor != _state.State.BlendStateCommon.AlphaSrcFactor ||
|
||||
entry.Alpha.AlphaDstFactor != _state.State.BlendStateCommon.AlphaDstFactor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptor = new AdvancedBlendDescriptor(entry.Op, entry.Overlap, entry.SrcPreMultiplied);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced blend function entry.
|
||||
/// </summary>
|
||||
struct AdvancedBlendEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced blend operation.
|
||||
/// </summary>
|
||||
public AdvancedBlendOp Op { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Advanced blend overlap mode.
|
||||
/// </summary>
|
||||
public AdvancedBlendOverlap Overlap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whenever the source input is pre-multiplied.
|
||||
/// </summary>
|
||||
public bool SrcPreMultiplied { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constants used by the microcode.
|
||||
/// </summary>
|
||||
public RgbFloat[] Constants { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Fixed function alpha state.
|
||||
/// </summary>
|
||||
public FixedFunctionAlpha Alpha { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new advanced blend function entry.
|
||||
/// </summary>
|
||||
/// <param name="op">Advanced blend operation</param>
|
||||
/// <param name="overlap">Advanced blend overlap mode</param>
|
||||
/// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param>
|
||||
/// <param name="constants">Constants used by the microcode</param>
|
||||
/// <param name="alpha">Fixed function alpha state</param>
|
||||
public AdvancedBlendEntry(
|
||||
AdvancedBlendOp op,
|
||||
AdvancedBlendOverlap overlap,
|
||||
bool srcPreMultiplied,
|
||||
RgbFloat[] constants,
|
||||
FixedFunctionAlpha alpha)
|
||||
{
|
||||
Op = op;
|
||||
Overlap = overlap;
|
||||
SrcPreMultiplied = srcPreMultiplied;
|
||||
Constants = constants;
|
||||
Alpha = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-generated hash table with advanced blend functions used by the driver.
|
||||
/// </summary>
|
||||
static class AdvancedBlendPreGenTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced blend functions dictionary.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyDictionary<Hash128, AdvancedBlendEntry> Entries = new Dictionary<Hash128, AdvancedBlendEntry>()
|
||||
{
|
||||
{ new Hash128(0x19ECF57B83DE31F7, 0x5BAE759246F264C0), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xDE1B14A356A1A9ED, 0x59D803593C607C1D), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x1A3C3A6D32DEC368, 0xBCAE519EC6AAA045), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x6FD380261A63B240, 0x17C3B335DBB9E3DB), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x1D39164823D3A2D1, 0xC45350959CE1C8FB), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x18DF09FF53B129FE, 0xC02EDA33C36019F6), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x5973E583271EBF06, 0x711497D75D1272E0), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x4759E0E5DA54D5E8, 0x1FDD57C0C38AFA1F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x337684D43CCE97FA, 0x0139E30CC529E1C9), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xDA59E85D8428992D, 0x1D3D7C64C9EF0132), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x9455B949298CE805, 0xE73D3301518BE98A), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xBDD3B4DEDBE336AA, 0xBFA4DCD50D535DEE), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x22D4E970A028649A, 0x4F3FCB055FCED965), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xA346A91311D72114, 0x151A27A3FB0A1904), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x8A307241061FACD6, 0xA39D1826440B8EE7), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xB3BE569485EFFFE0, 0x0BA4E269B3CFB165), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x36FCA3277DC11822, 0x2BC0F6CAC2029672), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x4A6226AF2DE9BD7F, 0xEB890D7DA716F73A), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xF364CAA94E160FEB, 0xBF364512C72A3797), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x6BF791AB4AC19C87, 0x6FA17A994EA0FCDE), new AdvancedBlendEntry(AdvancedBlendOp.InvertOvg, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x053C75A0AE0BB222, 0x03C791FEEB59754C), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x25762AB40B6CBDE9, 0x595E9A968AC4F01C), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xC2D05E2DBE16955D, 0xB8659C7A3FCFA7CE), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x223F220B8F74CBFB, 0xD3DD19D7C39209A5), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xD0DAE57A9F1FE78A, 0x353796BCFB8CE30B), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x601C8CBEC07FF8FF, 0xB8E22882360E8695), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x3A55B7B78C76A7A8, 0x206F503B2D9FFEAA), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x80BC65C7831388E5, 0xC652457B2C766AEC), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x3D3A912E5833EE13, 0x307895951349EE33), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x289105BE92E81803, 0xFD8F1F03D15C53B4), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x007AE3BD140764EB, 0x0EE05A0D2E80BBAE), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x77F7EE0DB3FDDB96, 0xDEA47C881306DB3E), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x66F4E9A7D73CA157, 0x1486058A177DB11C), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x593E9F331612D618, 0x9D217BEFA4EB919A), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x0A5194C5E6891106, 0xDD8EC6586106557C), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x8D77173D5E06E916, 0x06AB190E7D10F4D4), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x655B4EBC148981DA, 0x455999EF2B9BD28A), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x98F5437D5F518929, 0xBFF4A6E83183DB63), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x6ADDEFE3B9CEF2FD, 0xB6F6272AFECB1AAB), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x80953F0953BF05B1, 0xD59ABFAA34F8196F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xA401D9AA2A39C121, 0xFC0C8005C22AD7E3), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x06274FB7CA9CDD22, 0x6CE8188B1A9AB6EF), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x0B079BE7F7F70817, 0xB72E7736CA51E321), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x66215C99403CEDDE, 0x900B733D62204C48), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x12DEF2AD900CAD6C, 0x58CF5CC3004910DF), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x272BA3A49F64DAE4, 0xAC70B96C00A99EAF), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x206C34AAA7D3F545, 0xDA4B30CACAA483A0), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x3D93494920D257BE, 0xDCC573BE1F5F4449), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x0D7417D80191107B, 0xEAF40547827E005F), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xEC1B03E8C883F9C9, 0x2D3CA044C58C01B4), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x58A19A0135D68B31, 0x82F35B97AED068E5), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x20489F9AB36CC0E3, 0x20499874219E35EE), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xBB176935E5EE05BF, 0x95B26D4D30EA7A14), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x5FF9393C908ACFED, 0x068B0BD875773ABF), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x03181F8711C9802C, 0x6B02C7C6B224FE7B), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x2EE2209021F6B977, 0xF3AFA1491B8B89FC), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xD8BA4DD2EDE4DC9E, 0x01006114977CF715), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xD156B99835A2D8ED, 0x2D0BEE9E135EA7A7), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x20CE8C898ED4BE27, 0x1514900B6F5E8F66), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xCDE5F743820BA2D9, 0x917845FE2ECB083D), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xEB03DF4A0C1D14CD, 0xBAE2E831C6E8FFE4), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x1DC9E49AABC779AC, 0x4053A1441EB713D3), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xFBDEF776248F7B3E, 0xE05EEFD65AC47CB7), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x415A1A48E03AA6E7, 0x046D7EE33CA46B9A), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x59A6901EC9BB2041, 0x2F3E19CE5EEC3EBE), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x044B2B6E105221DA, 0x3089BBC033F994AF), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x374A5A24AA8E6CC5, 0x29930FAA6215FA2B), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x30CD0F7AF0CF26F9, 0x06CCA6744DE7DCF5), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x1A6C9A1F6FE494A5, 0xA0CFAF77617E54DD), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x081AF6DAAB1C8717, 0xBFEDCE59AE3DC9AC), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x3518E44573AB68BA, 0xC96EE71AF9F8F546), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xF89E81FE8D73C96F, 0x4583A04577A0F21C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xDF4026421CB61119, 0x14115A1F5139AFC7), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x91A20262C3E3A695, 0x0B3A102BFCDC6B1C), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x44F4C7CCFEB9EBFA, 0xF68394E6D56E5C2F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xB89F17C7021E9760, 0x430357EE0F7188EF), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xDA2D20EA4242B8A0, 0x0D1EC05B72E3838F), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x855DFEE1208D11B9, 0x77C6E3DDCFE30B85), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x9B3808439683FD58, 0x123DCBE4705AB25E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xA42CF045C248A00A, 0x0C6C63C24EA0B0C1), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x320A83B6D00C8059, 0x796EDAB3EB7314BC), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x45253AC9ABFFC613, 0x8F92EA70195FB573), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x1A5D263B588274B6, 0x167D305F6C794179), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x709C1A837FE966AC, 0x75D8CE49E8A78EDB), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x8265C26F85E4145F, 0x932E6CCBF37CB600), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x3F252B3FEF983F27, 0x9370D7EEFEFA1A9E), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x66A334A4AEA41078, 0xCB52254E1E395231), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xFDD05C53B25F0035, 0xB7E3ECEE166C222F), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x25D932A77FFED81A, 0xA50D797B0FCA94E8), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x4A953B6F5F7D341C, 0xDC05CFB50DDB5DC1), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x838CB660C4F41F6D, 0x9E7D958697543495), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x4DF6EC1348A8F797, 0xA128E0CD69DB5A64), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x178CDFAB9A015295, 0x2BF40EA72E596D57), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x338FC99050E56AFD, 0x2AF41CF82BE602BF), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x62E02ED60D1E978E, 0xBF726B3E68C11E4D), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xFBAF92DD4C101502, 0x7AF2EDA6596B819D), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x0EF1241F65D4B50A, 0xE8D85DFA6AEDDB84), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x77FE024B5C9D4A18, 0xF19D48A932F6860F), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x9C88CBFA2E09D857, 0x0A0361704CBEEE1D), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x5B94127FA190E640, 0x8D1FEFF837A91268), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xB9C9105B7E063DDB, 0xF6A70E1D511B96FD), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xF0751AAE332B3ED1, 0xC40146F5C83C2533), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x579EB12F595F75AD, 0x151BF0504703B81B), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xF9CA152C03AC8C62, 0x1581336205E5CF47), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.DstAlphaGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x98ACD8BB5E195D0F, 0x91F937672BE899F0), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0xBF97F10FC301F44C, 0x75721789F0D48548), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x1B982263B8B08A10, 0x3350C76E2E1B27DF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0xFF20AC79F64EDED8, 0xAF9025B2D97B9273), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x9FFD986600FB112F, 0x384FDDF4E060139A), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x0425E40B5B8B3B52, 0x5880CBED7CAB631C), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x16DAC8593F28623A, 0x233DBC82325B8AED), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xB37E5F234B9F0948, 0xD5F957A2ECD98FD6), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xCA0FDADD1D20DBE3, 0x1A5C15CCBF1AC538), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x1C48304D73A9DF3A, 0x891DB93FA36E3450), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x53200F2279B7FA39, 0x051C2462EBF6789C), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xB88BFB80714DCD5C, 0xEBD6938D744E6A41), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xE33DC2A25FC1A976, 0x08B3DBB1F3027D45), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xCE97E71615370316, 0xE131AE49D3A4D62B), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xE059FD265149B256, 0x94AF817AC348F61F), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x16D31333D477E231, 0x9A98AAC84F72CC62), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x47FC3B0776366D3C, 0xE96D9BD83B277874), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x7230401E3FEA1F3B, 0xF0D15F05D3D1E309), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x188212F9303742F5, 0x100C51CB96E03591), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x52B755D296B44DC5, 0x4003B87275625973), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xD873ED973ADF7EAD, 0x73E68B57D92034E7), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x471F9FA34B945ACB, 0x10524D1410B3C402), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x99F569454EA0EF32, 0x6FC70A8B3A07DC8B), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x5AD55F950067AC7E, 0x4BA60A4FBABDD0AC), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x03FF2C858C9C4C5B, 0xE95AE7F561FB60E9), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x6DC0E510C7BCF9D2, 0xAE805D7CECDCB5C1), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x44832332CED5C054, 0x2F8D5536C085B30A), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x4AB4D387618AC51F, 0x495B46E0555F4B32), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x99282B49405A01A8, 0xD6FA93F864F24A8E), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x37B30C1064FBD23E, 0x5D068366F42317C2), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x760FAE9D59E04BC2, 0xA40AD483EA01435E), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xE786950FD9D1C6EF, 0xF9FDD5AF6451D239), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x052458BB4788B0CA, 0x8AC58FDCA1F45EF5), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x6AFC3837D1D31920, 0xB9D49C2FE49642C6), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0xAFC2911949317E01, 0xD5B63636F5CB3422), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
|
||||
{ new Hash128(0x13B46DF507CC2C53, 0x86DE26517E6BF0A7), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x5C372442474BE410, 0x79ECD3C0C496EF2E), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x74AAB45DBF5336E9, 0x01BFC4E181DAD442), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x43239E282A36C85C, 0x36FB65560E46AD0F), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x1A3BA8A7583B8F7A, 0xE64E41D548033180), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x32BBB9859E9B565D, 0x3D5CE94FE55F18B5), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xD947A0766AE3C0FC, 0x391E5D53E86F4ED6), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0xBD9A7C08BDFD8CE6, 0x905407634901355E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x8395475BCB0D7A8C, 0x48AF5DD501D44A70), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x80AAC23FEBD4A3E5, 0xEA8C70F0B4DE52DE), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x2F3AD1B0F1B3FD09, 0xC0EBC784BFAB8EA3), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x52B54032F2F70BFF, 0xC941D6FDED674765), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xCA7B86F72EC6A99B, 0x55868A131AFE359E), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x377919B60BD133CA, 0x0FD611627664EF40), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x9D4A0C5EE1153887, 0x7B869EBA218C589B), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x311F2A858545D123, 0xB4D09C802480AD62), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xCF78AA6A83AFA689, 0x9DC48B0C2182A3E1), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xC3018CD6F1CF62D1, 0x016E32DD9087B1BB), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x9CB62CE0E956EE29, 0x0FB67F503E60B3AD), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x3589A13C16EF3BFA, 0x15B29BFC91F3BDFB), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x3502CA5FB7529917, 0xFA51BFD0D1688071), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x62ADC25AD6D0A923, 0x76CB6D238276D3A3), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x09FDEB1116A9D52C, 0x85BB8627CD5C2733), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x0709FED1B65E18EB, 0x5BC3AA4D99EC19CF), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xB18D28AE5DE4C723, 0xE820AA2B75C9C02E), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x6743C51621497480, 0x4B164E40858834AE), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x63D1E181E34A2944, 0x1AE292C9D9F12819), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x079523298250BFF6, 0xC0C793510603CDB5), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x4C9D0A973C805EA6, 0xD1FF59AD5156B93C), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x1E914678F3057BCD, 0xD503AE389C12D229), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0x9FDBADE5556C5311, 0x03F0CBC798FC5C94), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xE39451534635403C, 0x606CC1CA1F452388), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x1D39F0F0A1008AA6, 0xBFDF2B97E6C3F125), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xDB81BED30D5BDBEA, 0xAF0B2856EB93AD2C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x83F69CCF1D0A79B6, 0x70D31332797430AC), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x7B87F807AB7A8F5C, 0x1241A2A01FB31771), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xF557172E20D5272D, 0xC1961F8C7A5D2820), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xA8476B3944DBBC9B, 0x84A2F6AF97B15FDF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
|
||||
{ new Hash128(0x3259602B55414DA3, 0x72AACCC00B5A9D10), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
|
||||
{ new Hash128(0xC0CB8C10F36EDCD6, 0x8C2D088AD8191E1C), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x81806C451C6255EF, 0x5AA8AC9A08941A15), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xE55A6537F4568198, 0xCA8735390B799B19), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x5C044BA14536DDA3, 0xBCE0123ED7D510EC), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x6788346C405BE130, 0x372A4BB199C01F9F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x510EDC2A34E2856B, 0xE1727A407E294254), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x4B7BE01BD398C7A8, 0x5BFF79BC00672C18), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x213B43845540CFEC, 0xDA857411CF1CCFCE), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x765AFA6732E783F1, 0x8F1CABF1BC78A014), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xA4A5DE1CC06F6CB1, 0xA0634A0011001709), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x81F32BD8816EA796, 0x697EE86683165170), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xB870C209EAA5F092, 0xAF5FD923909CAA1F), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x3649A9F5C936FB83, 0xDD7C834897AA182A), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xD72A2B1097A5995C, 0x3D41B2763A913654), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x551E212B9F6C454A, 0xB0DFA05BEB3C37FA), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x681B5A313B7416BF, 0xCB1CBAEEB4D81500), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x9343A18BD4B16777, 0xEDB4AC1C8972C3A4), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xC960BF6D8519DE28, 0x78D8557FD405D119), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x65A7B01FDC73A46C, 0x297E096ED5CC4D8A), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0xD9C99BA4A6CDC13B, 0x3CFF0ACEDC2EE150), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x6BC00DA6EB922BD1, 0x5FD4C11F2A685234), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
{ new Hash128(0x8652300E32D93050, 0x9460E7B449132371), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed function alpha state used for a advanced blend function.
|
||||
/// </summary>
|
||||
struct FixedFunctionAlpha
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed function alpha state with alpha blending disabled.
|
||||
/// </summary>
|
||||
public static FixedFunctionAlpha Disabled => new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, default, default, default);
|
||||
|
||||
/// <summary>
|
||||
/// Individual enable bits for the RGB and alpha components.
|
||||
/// </summary>
|
||||
public BlendUcodeEnable Enable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Alpha blend operation.
|
||||
/// </summary>
|
||||
public BlendOp AlphaOp { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Value multiplied with the blend source operand.
|
||||
/// </summary>
|
||||
public BlendFactor AlphaSrcFactor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Value multiplied with the blend destination operand.
|
||||
/// </summary>
|
||||
public BlendFactor AlphaDstFactor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new blend fixed function alpha state.
|
||||
/// </summary>
|
||||
/// <param name="enable">Individual enable bits for the RGB and alpha components</param>
|
||||
/// <param name="alphaOp">Alpha blend operation</param>
|
||||
/// <param name="alphaSrc">Value multiplied with the blend source operand</param>
|
||||
/// <param name="alphaDst">Value multiplied with the blend destination operand</param>
|
||||
public FixedFunctionAlpha(BlendUcodeEnable enable, BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst)
|
||||
{
|
||||
Enable = enable;
|
||||
AlphaOp = alphaOp;
|
||||
AlphaSrcFactor = alphaSrc;
|
||||
AlphaDstFactor = alphaDst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new blend fixed function alpha state.
|
||||
/// </summary>
|
||||
/// <param name="alphaOp">Alpha blend operation</param>
|
||||
/// <param name="alphaSrc">Value multiplied with the blend source operand</param>
|
||||
/// <param name="alphaDst">Value multiplied with the blend destination operand</param>
|
||||
public FixedFunctionAlpha(BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst) : this(BlendUcodeEnable.EnableRGB, alphaOp, alphaSrc, alphaDst)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode assembly function delegate.
|
||||
/// </summary>
|
||||
/// <param name="asm">Assembler</param>
|
||||
/// <returns>Fixed function alpha state for the microcode</returns>
|
||||
delegate FixedFunctionAlpha GenUcodeFunc(ref UcodeAssembler asm);
|
||||
|
||||
/// <summary>
|
||||
/// Advanced blend microcode state.
|
||||
/// </summary>
|
||||
struct AdvancedBlendUcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced blend operation.
|
||||
/// </summary>
|
||||
public AdvancedBlendOp Op { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Advanced blend overlap mode.
|
||||
/// </summary>
|
||||
public AdvancedBlendOverlap Overlap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whenever the source input is pre-multiplied.
|
||||
/// </summary>
|
||||
public bool SrcPreMultiplied { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Fixed function alpha state.
|
||||
/// </summary>
|
||||
public FixedFunctionAlpha Alpha { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Microcode.
|
||||
/// </summary>
|
||||
public uint[] Code { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constants used by the microcode.
|
||||
/// </summary>
|
||||
public RgbFloat[] Constants { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new advanced blend state.
|
||||
/// </summary>
|
||||
/// <param name="op">Advanced blend operation</param>
|
||||
/// <param name="overlap">Advanced blend overlap mode</param>
|
||||
/// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param>
|
||||
/// <param name="genFunc">Function that will generate the advanced blend microcode</param>
|
||||
public AdvancedBlendUcode(
|
||||
AdvancedBlendOp op,
|
||||
AdvancedBlendOverlap overlap,
|
||||
bool srcPreMultiplied,
|
||||
GenUcodeFunc genFunc)
|
||||
{
|
||||
Op = op;
|
||||
Overlap = overlap;
|
||||
SrcPreMultiplied = srcPreMultiplied;
|
||||
|
||||
UcodeAssembler asm = new UcodeAssembler();
|
||||
Alpha = genFunc(ref asm);
|
||||
Code = asm.GetCode();
|
||||
Constants = asm.GetConstants();
|
||||
}
|
||||
}
|
||||
}
|
305
src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs
Normal file
305
src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs
Normal file
|
@ -0,0 +1,305 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
|
||||
{
|
||||
/// <summary>
|
||||
/// Blend microcode instruction.
|
||||
/// </summary>
|
||||
enum Instruction
|
||||
{
|
||||
Mmadd = 0,
|
||||
Mmsub = 1,
|
||||
Min = 2,
|
||||
Max = 3,
|
||||
Rcp = 4,
|
||||
Add = 5,
|
||||
Sub = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode condition code.
|
||||
/// </summary>
|
||||
enum CC
|
||||
{
|
||||
F = 0,
|
||||
T = 1,
|
||||
EQ = 2,
|
||||
NE = 3,
|
||||
LT = 4,
|
||||
LE = 5,
|
||||
GT = 6,
|
||||
GE = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode opend B or D value.
|
||||
/// </summary>
|
||||
enum OpBD
|
||||
{
|
||||
ConstantZero = 0x0,
|
||||
ConstantOne = 0x1,
|
||||
SrcRGB = 0x2,
|
||||
SrcAAA = 0x3,
|
||||
OneMinusSrcAAA = 0x4,
|
||||
DstRGB = 0x5,
|
||||
DstAAA = 0x6,
|
||||
OneMinusDstAAA = 0x7,
|
||||
Temp0 = 0x9,
|
||||
Temp1 = 0xa,
|
||||
Temp2 = 0xb,
|
||||
PBR = 0xc,
|
||||
ConstantRGB = 0xd
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode operand A or C value.
|
||||
/// </summary>
|
||||
enum OpAC
|
||||
{
|
||||
SrcRGB = 0,
|
||||
DstRGB = 1,
|
||||
SrcAAA = 2,
|
||||
DstAAA = 3,
|
||||
Temp0 = 4,
|
||||
Temp1 = 5,
|
||||
Temp2 = 6,
|
||||
PBR = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode destination operand.
|
||||
/// </summary>
|
||||
enum OpDst
|
||||
{
|
||||
Temp0 = 0,
|
||||
Temp1 = 1,
|
||||
Temp2 = 2,
|
||||
PBR = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode input swizzle.
|
||||
/// </summary>
|
||||
enum Swizzle
|
||||
{
|
||||
RGB = 0,
|
||||
GBR = 1,
|
||||
RRR = 2,
|
||||
GGG = 3,
|
||||
BBB = 4,
|
||||
RToA = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode output components.
|
||||
/// </summary>
|
||||
enum WriteMask
|
||||
{
|
||||
RGB = 0,
|
||||
R = 1,
|
||||
G = 2,
|
||||
B = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Floating-point RGB color values.
|
||||
/// </summary>
|
||||
struct RgbFloat
|
||||
{
|
||||
/// <summary>
|
||||
/// Red component value.
|
||||
/// </summary>
|
||||
public float R { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Green component value.
|
||||
/// </summary>
|
||||
public float G { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Blue component value.
|
||||
/// </summary>
|
||||
public float B { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new floating-point RGB value.
|
||||
/// </summary>
|
||||
/// <param name="r">Red component value</param>
|
||||
/// <param name="g">Green component value</param>
|
||||
/// <param name="b">Blue component value</param>
|
||||
public RgbFloat(float r, float g, float b)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode destination operand, including swizzle, write mask and condition code update flag.
|
||||
/// </summary>
|
||||
struct Dest
|
||||
{
|
||||
public static Dest Temp0 => new Dest(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false);
|
||||
public static Dest Temp1 => new Dest(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false);
|
||||
public static Dest Temp2 => new Dest(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false);
|
||||
public static Dest PBR => new Dest(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false);
|
||||
|
||||
public Dest GBR => new Dest(Dst, Swizzle.GBR, WriteMask, WriteCC);
|
||||
public Dest RRR => new Dest(Dst, Swizzle.RRR, WriteMask, WriteCC);
|
||||
public Dest GGG => new Dest(Dst, Swizzle.GGG, WriteMask, WriteCC);
|
||||
public Dest BBB => new Dest(Dst, Swizzle.BBB, WriteMask, WriteCC);
|
||||
public Dest RToA => new Dest(Dst, Swizzle.RToA, WriteMask, WriteCC);
|
||||
|
||||
public Dest R => new Dest(Dst, Swizzle, WriteMask.R, WriteCC);
|
||||
public Dest G => new Dest(Dst, Swizzle, WriteMask.G, WriteCC);
|
||||
public Dest B => new Dest(Dst, Swizzle, WriteMask.B, WriteCC);
|
||||
|
||||
public Dest CC => new Dest(Dst, Swizzle, WriteMask, true);
|
||||
|
||||
public OpDst Dst { get; }
|
||||
public Swizzle Swizzle { get; }
|
||||
public WriteMask WriteMask { get; }
|
||||
public bool WriteCC { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new blend microcode destination operand.
|
||||
/// </summary>
|
||||
/// <param name="dst">Operand</param>
|
||||
/// <param name="swizzle">Swizzle</param>
|
||||
/// <param name="writeMask">Write maks</param>
|
||||
/// <param name="writeCC">Indicates if condition codes should be updated</param>
|
||||
public Dest(OpDst dst, Swizzle swizzle, WriteMask writeMask, bool writeCC)
|
||||
{
|
||||
Dst = dst;
|
||||
Swizzle = swizzle;
|
||||
WriteMask = writeMask;
|
||||
WriteCC = writeCC;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode operaiton.
|
||||
/// </summary>
|
||||
struct UcodeOp
|
||||
{
|
||||
public readonly uint Word;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new blend microcode operation.
|
||||
/// </summary>
|
||||
/// <param name="cc">Condition code that controls whenever the operation is executed or not</param>
|
||||
/// <param name="inst">Instruction</param>
|
||||
/// <param name="constIndex">Index on the constant table of the constant used by any constant operand</param>
|
||||
/// <param name="dest">Destination operand</param>
|
||||
/// <param name="srcA">First input operand</param>
|
||||
/// <param name="srcB">Second input operand</param>
|
||||
/// <param name="srcC">Third input operand</param>
|
||||
/// <param name="srcD">Fourth input operand</param>
|
||||
public UcodeOp(CC cc, Instruction inst, int constIndex, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
|
||||
{
|
||||
Word = (uint)cc |
|
||||
((uint)inst << 3) |
|
||||
((uint)constIndex << 6) |
|
||||
((uint)srcA << 9) |
|
||||
((uint)srcB << 12) |
|
||||
((uint)srcC << 16) |
|
||||
((uint)srcD << 19) |
|
||||
((uint)dest.Swizzle << 23) |
|
||||
((uint)dest.WriteMask << 26) |
|
||||
((uint)dest.Dst << 28) |
|
||||
(dest.WriteCC ? (1u << 31) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend microcode assembler.
|
||||
/// </summary>
|
||||
struct UcodeAssembler
|
||||
{
|
||||
private List<uint> _code;
|
||||
private RgbFloat[] _constants;
|
||||
private int _constantIndex;
|
||||
|
||||
public void Mul(CC cc, Dest dest, OpAC srcA, OpBD srcB)
|
||||
{
|
||||
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
|
||||
}
|
||||
|
||||
public void Madd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC)
|
||||
{
|
||||
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, OpBD.ConstantOne);
|
||||
}
|
||||
|
||||
public void Mmadd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
|
||||
{
|
||||
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, srcD);
|
||||
}
|
||||
|
||||
public void Mmsub(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
|
||||
{
|
||||
Assemble(cc, Instruction.Mmsub, dest, srcA, srcB, srcC, srcD);
|
||||
}
|
||||
|
||||
public void Min(CC cc, Dest dest, OpAC srcA, OpBD srcB)
|
||||
{
|
||||
Assemble(cc, Instruction.Min, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
|
||||
}
|
||||
|
||||
public void Max(CC cc, Dest dest, OpAC srcA, OpBD srcB)
|
||||
{
|
||||
Assemble(cc, Instruction.Max, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
|
||||
}
|
||||
|
||||
public void Rcp(CC cc, Dest dest, OpAC srcA)
|
||||
{
|
||||
Assemble(cc, Instruction.Rcp, dest, srcA, OpBD.ConstantZero, OpAC.SrcRGB, OpBD.ConstantZero);
|
||||
}
|
||||
|
||||
public void Mov(CC cc, Dest dest, OpBD srcB)
|
||||
{
|
||||
Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
|
||||
}
|
||||
|
||||
public void Add(CC cc, Dest dest, OpBD srcB, OpBD srcD)
|
||||
{
|
||||
Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD);
|
||||
}
|
||||
|
||||
public void Sub(CC cc, Dest dest, OpBD srcB, OpBD srcD)
|
||||
{
|
||||
Assemble(cc, Instruction.Sub, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD);
|
||||
}
|
||||
|
||||
private void Assemble(CC cc, Instruction inst, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
|
||||
{
|
||||
(_code ??= new List<uint>()).Add(new UcodeOp(cc, inst, _constantIndex, dest, srcA, srcB, srcC, srcD).Word);
|
||||
}
|
||||
|
||||
public void SetConstant(int index, float r, float g, float b)
|
||||
{
|
||||
if (_constants == null)
|
||||
{
|
||||
_constants = new RgbFloat[index + 1];
|
||||
}
|
||||
else if (_constants.Length <= index)
|
||||
{
|
||||
Array.Resize(ref _constants, index + 1);
|
||||
}
|
||||
|
||||
_constants[index] = new RgbFloat(r, g, b);
|
||||
_constantIndex = index;
|
||||
}
|
||||
|
||||
public uint[] GetCode()
|
||||
{
|
||||
return _code?.ToArray();
|
||||
}
|
||||
|
||||
public RgbFloat[] GetConstants()
|
||||
{
|
||||
return _constants;
|
||||
}
|
||||
}
|
||||
}
|
130
src/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
Normal file
130
src/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods used for conditional rendering.
|
||||
/// </summary>
|
||||
static class ConditionalRendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if draws and clears should be performed, according
|
||||
/// to currently set conditional rendering conditions.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
|
||||
/// <param name="address">Conditional rendering buffer address</param>
|
||||
/// <param name="condition">Conditional rendering condition</param>
|
||||
/// <returns>True if rendering is enabled, false otherwise</returns>
|
||||
public static ConditionalRenderEnabled GetRenderEnable(GpuContext context, MemoryManager memoryManager, GpuVa address, Condition condition)
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case Condition.Always:
|
||||
return ConditionalRenderEnabled.True;
|
||||
case Condition.Never:
|
||||
return ConditionalRenderEnabled.False;
|
||||
case Condition.ResultNonZero:
|
||||
return CounterNonZero(context, memoryManager, address.Pack());
|
||||
case Condition.Equal:
|
||||
return CounterCompare(context, memoryManager, address.Pack(), true);
|
||||
case Condition.NotEqual:
|
||||
return CounterCompare(context, memoryManager, address.Pack(), false);
|
||||
}
|
||||
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condition}\".");
|
||||
|
||||
return ConditionalRenderEnabled.True;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the counter value at a given GPU memory address is non-zero.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the counter value</param>
|
||||
/// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
|
||||
private static ConditionalRenderEnabled CounterNonZero(GpuContext context, MemoryManager memoryManager, ulong gpuVa)
|
||||
{
|
||||
ICounterEvent evt = memoryManager.CounterCache.FindEvent(gpuVa);
|
||||
|
||||
if (evt == null)
|
||||
{
|
||||
return ConditionalRenderEnabled.False;
|
||||
}
|
||||
|
||||
if (context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
|
||||
{
|
||||
return ConditionalRenderEnabled.Host;
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.Flush();
|
||||
return (memoryManager.Read<ulong>(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
|
||||
/// <param name="gpuVa">GPU virtual address</param>
|
||||
/// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
|
||||
/// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
|
||||
private static ConditionalRenderEnabled CounterCompare(GpuContext context, MemoryManager memoryManager, ulong gpuVa, bool isEqual)
|
||||
{
|
||||
ICounterEvent evt = FindEvent(memoryManager.CounterCache, gpuVa);
|
||||
ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16);
|
||||
|
||||
bool useHost;
|
||||
|
||||
if (evt != null && evt2 == null)
|
||||
{
|
||||
useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, memoryManager.Read<ulong>(gpuVa + 16), isEqual);
|
||||
}
|
||||
else if (evt == null && evt2 != null)
|
||||
{
|
||||
useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt2, memoryManager.Read<ulong>(gpuVa), isEqual);
|
||||
}
|
||||
else if (evt != null && evt2 != null)
|
||||
{
|
||||
useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
|
||||
}
|
||||
else
|
||||
{
|
||||
useHost = false;
|
||||
}
|
||||
|
||||
if (useHost)
|
||||
{
|
||||
return ConditionalRenderEnabled.Host;
|
||||
}
|
||||
else
|
||||
{
|
||||
evt?.Flush();
|
||||
evt2?.Flush();
|
||||
|
||||
ulong x = memoryManager.Read<ulong>(gpuVa, true);
|
||||
ulong y = memoryManager.Read<ulong>(gpuVa + 16, true);
|
||||
|
||||
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a counter that is supposed to be written at the specified address,
|
||||
/// returning the related event.
|
||||
/// </summary>
|
||||
/// <param name="counterCache">GPU counter cache to search on</param>
|
||||
/// <param name="gpuVa">GPU virtual address where the counter is supposed to be written</param>
|
||||
/// <returns>The counter event, or null if not present</returns>
|
||||
private static ICounterEvent FindEvent(CounterCache counterCache, ulong gpuVa)
|
||||
{
|
||||
return counterCache.FindEvent(gpuVa);
|
||||
}
|
||||
}
|
||||
}
|
183
src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
Normal file
183
src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
Normal file
|
@ -0,0 +1,183 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant buffer updater.
|
||||
/// </summary>
|
||||
class ConstantBufferUpdater
|
||||
{
|
||||
private const int UniformDataCacheSize = 512;
|
||||
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
|
||||
// State associated with direct uniform buffer updates.
|
||||
// This state is used to attempt to batch together consecutive updates.
|
||||
private ulong _ubBeginCpuAddress = 0;
|
||||
private ulong _ubFollowUpAddress = 0;
|
||||
private ulong _ubByteCount = 0;
|
||||
private int _ubIndex = 0;
|
||||
private int[] _ubData = new int[UniformDataCacheSize];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the constant buffer updater.
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Channel state</param>
|
||||
public ConstantBufferUpdater(GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
|
||||
{
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the vertex shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void BindVertex(int argument)
|
||||
{
|
||||
Bind(argument, ShaderType.Vertex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation control shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void BindTessControl(int argument)
|
||||
{
|
||||
Bind(argument, ShaderType.TessellationControl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation evaluation shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void BindTessEvaluation(int argument)
|
||||
{
|
||||
Bind(argument, ShaderType.TessellationEvaluation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the geometry shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void BindGeometry(int argument)
|
||||
{
|
||||
Bind(argument, ShaderType.Geometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the fragment shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void BindFragment(int argument)
|
||||
{
|
||||
Bind(argument, ShaderType.Fragment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the specified shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="type">Shader stage that will access the uniform buffer</param>
|
||||
private void Bind(int argument, ShaderType type)
|
||||
{
|
||||
bool enable = (argument & 1) != 0;
|
||||
|
||||
int index = (argument >> 4) & 0x1f;
|
||||
|
||||
FlushUboDirty();
|
||||
|
||||
if (enable)
|
||||
{
|
||||
var uniformBuffer = _state.State.UniformBufferState;
|
||||
|
||||
ulong address = uniformBuffer.Address.Pack();
|
||||
|
||||
_channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
_channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes any queued UBO updates.
|
||||
/// </summary>
|
||||
public void FlushUboDirty()
|
||||
{
|
||||
if (_ubFollowUpAddress != 0)
|
||||
{
|
||||
var memoryManager = _channel.MemoryManager;
|
||||
|
||||
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
||||
|
||||
if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
|
||||
{
|
||||
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
||||
}
|
||||
|
||||
_ubFollowUpAddress = 0;
|
||||
_ubIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the uniform buffer data with inline data.
|
||||
/// </summary>
|
||||
/// <param name="argument">New uniform buffer data word</param>
|
||||
public void Update(int argument)
|
||||
{
|
||||
var uniformBuffer = _state.State.UniformBufferState;
|
||||
|
||||
ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
|
||||
|
||||
if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length)
|
||||
{
|
||||
FlushUboDirty();
|
||||
|
||||
_ubByteCount = 0;
|
||||
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
||||
}
|
||||
|
||||
_ubData[_ubIndex++] = argument;
|
||||
|
||||
_ubFollowUpAddress = address + 4;
|
||||
_ubByteCount += 4;
|
||||
|
||||
_state.State.UniformBufferState.Offset += 4;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the uniform buffer data with inline data.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to be written to the uniform buffer</param>
|
||||
public void Update(ReadOnlySpan<int> data)
|
||||
{
|
||||
var uniformBuffer = _state.State.UniformBufferState;
|
||||
|
||||
ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
|
||||
|
||||
ulong size = (ulong)data.Length * 4;
|
||||
|
||||
if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length)
|
||||
{
|
||||
FlushUboDirty();
|
||||
|
||||
_ubByteCount = 0;
|
||||
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
||||
}
|
||||
|
||||
data.CopyTo(_ubData.AsSpan(_ubIndex));
|
||||
_ubIndex += data.Length;
|
||||
|
||||
_ubFollowUpAddress = address + size;
|
||||
_ubByteCount += size;
|
||||
|
||||
_state.State.UniformBufferState.Offset += data.Length * 4;
|
||||
}
|
||||
}
|
||||
}
|
856
src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
Normal file
856
src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
Normal file
|
@ -0,0 +1,856 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw manager.
|
||||
/// </summary>
|
||||
class DrawManager
|
||||
{
|
||||
// Since we don't know the index buffer size for indirect draws,
|
||||
// we must assume a minimum and maximum size and use that for buffer data update purposes.
|
||||
private const int MinIndirectIndexCount = 0x10000;
|
||||
private const int MaxIndirectIndexCount = 0x4000000;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
private readonly DrawState _drawState;
|
||||
private readonly SpecializationStateUpdater _currentSpecState;
|
||||
private bool _topologySet;
|
||||
|
||||
private bool _instancedDrawPending;
|
||||
private bool _instancedIndexed;
|
||||
private bool _instancedIndexedInline;
|
||||
|
||||
private int _instancedFirstIndex;
|
||||
private int _instancedFirstVertex;
|
||||
private int _instancedFirstInstance;
|
||||
private int _instancedIndexCount;
|
||||
private int _instancedDrawStateFirst;
|
||||
private int _instancedDrawStateCount;
|
||||
|
||||
private int _instanceIndex;
|
||||
|
||||
private const int VertexBufferFirstMethodOffset = 0x35d;
|
||||
private const int IndexBufferCountMethodOffset = 0x5f8;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the draw manager.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Channel state</param>
|
||||
/// <param name="drawState">Draw state</param>
|
||||
/// <param name="spec">Specialization state updater</param>
|
||||
public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec)
|
||||
{
|
||||
_context = context;
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
_drawState = drawState;
|
||||
_currentSpecState = spec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the entire state as dirty, forcing a full host state update before the next draw.
|
||||
/// </summary>
|
||||
public void ForceStateDirty()
|
||||
{
|
||||
_topologySet = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes four 8-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU8(int argument)
|
||||
{
|
||||
_drawState.IbStreamer.VbElementU8(_context.Renderer, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes two 16-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU16(int argument)
|
||||
{
|
||||
_drawState.IbStreamer.VbElementU16(_context.Renderer, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes one 32-bit index buffer element.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU32(int argument)
|
||||
{
|
||||
_drawState.IbStreamer.VbElementU32(_context.Renderer, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the draw call.
|
||||
/// This draws geometry on the bound buffers based on the current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawEnd(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawEnd(
|
||||
engine,
|
||||
_state.State.IndexBufferState.First,
|
||||
(int)_state.State.IndexBufferCount,
|
||||
_state.State.VertexBufferDrawState.First,
|
||||
_state.State.VertexBufferDrawState.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the draw call.
|
||||
/// This draws geometry on the bound buffers based on the current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
|
||||
/// <param name="indexCount">Number of index buffer elements used on the draw</param>
|
||||
/// <param name="drawFirstVertex">Index of the first vertex used on the draw</param>
|
||||
/// <param name="drawVertexCount">Number of vertices used on the draw</param>
|
||||
private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount, int drawFirstVertex, int drawVertexCount)
|
||||
{
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
_channel.MemoryManager,
|
||||
_state.State.RenderEnableAddress,
|
||||
_state.State.RenderEnableCondition);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
|
||||
{
|
||||
if (renderEnable == ConditionalRenderEnabled.False)
|
||||
{
|
||||
PerformDeferredDraws();
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_drawState.FirstIndex = firstIndex;
|
||||
_drawState.IndexCount = indexCount;
|
||||
_drawState.DrawFirstVertex = drawFirstVertex;
|
||||
_drawState.DrawVertexCount = drawVertexCount;
|
||||
_currentSpecState.SetHasConstantBufferDrawParameters(false);
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
bool instanced = _drawState.VsUsesInstanceId || _drawState.IsAnyVbInstanced;
|
||||
|
||||
if (instanced)
|
||||
{
|
||||
_instancedDrawPending = true;
|
||||
|
||||
int ibCount = _drawState.IbStreamer.InlineIndexCount;
|
||||
|
||||
_instancedIndexed = _drawState.DrawIndexed;
|
||||
_instancedIndexedInline = ibCount != 0;
|
||||
|
||||
_instancedFirstIndex = firstIndex;
|
||||
_instancedFirstVertex = (int)_state.State.FirstVertex;
|
||||
_instancedFirstInstance = (int)_state.State.FirstInstance;
|
||||
|
||||
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
|
||||
|
||||
_instancedDrawStateFirst = drawFirstVertex;
|
||||
_instancedDrawStateCount = drawVertexCount;
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int firstInstance = (int)_state.State.FirstInstance;
|
||||
|
||||
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
|
||||
|
||||
if (inlineIndexCount != 0)
|
||||
{
|
||||
int firstVertex = (int)_state.State.FirstVertex;
|
||||
|
||||
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
|
||||
|
||||
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
|
||||
|
||||
_context.Renderer.Pipeline.DrawIndexed(inlineIndexCount, 1, firstIndex, firstVertex, firstInstance);
|
||||
}
|
||||
else if (_drawState.DrawIndexed)
|
||||
{
|
||||
int firstVertex = (int)_state.State.FirstVertex;
|
||||
|
||||
_context.Renderer.Pipeline.DrawIndexed(indexCount, 1, firstIndex, firstVertex, firstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
var drawState = _state.State.VertexBufferDrawState;
|
||||
|
||||
_context.Renderer.Pipeline.Draw(drawVertexCount, 1, drawFirstVertex, firstInstance);
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts draw.
|
||||
/// This sets primitive type and instanced draw parameters.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawBegin(int argument)
|
||||
{
|
||||
bool incrementInstance = (argument & (1 << 26)) != 0;
|
||||
bool resetInstance = (argument & (1 << 27)) == 0;
|
||||
|
||||
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
|
||||
DrawBegin(incrementInstance, resetInstance, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts draw.
|
||||
/// This sets primitive type and instanced draw parameters.
|
||||
/// </summary>
|
||||
/// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
|
||||
/// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
|
||||
/// <param name="primitiveType">Primitive type</param>
|
||||
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
|
||||
{
|
||||
if (incrementInstance)
|
||||
{
|
||||
_instanceIndex++;
|
||||
}
|
||||
else if (resetInstance)
|
||||
{
|
||||
PerformDeferredDraws();
|
||||
|
||||
_instanceIndex = 0;
|
||||
}
|
||||
|
||||
PrimitiveTopology topology;
|
||||
|
||||
if (_state.State.PrimitiveTypeOverrideEnable)
|
||||
{
|
||||
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
|
||||
topology = typeOverride.Convert();
|
||||
}
|
||||
else
|
||||
{
|
||||
topology = primitiveType.Convert();
|
||||
}
|
||||
|
||||
UpdateTopology(topology);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the current primitive topology if needed.
|
||||
/// </summary>
|
||||
/// <param name="topology">New primitive topology</param>
|
||||
private void UpdateTopology(PrimitiveTopology topology)
|
||||
{
|
||||
if (_drawState.Topology != topology || !_topologySet)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetPrimitiveTopology(topology);
|
||||
_currentSpecState.SetTopology(topology);
|
||||
_drawState.Topology = topology;
|
||||
_topologySet = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index buffer count.
|
||||
/// This also sets internal state that indicates that the next draw is an indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void SetIndexBufferCount(int argument)
|
||||
{
|
||||
_drawState.DrawIndexed = true;
|
||||
}
|
||||
|
||||
// TODO: Verify if the index type is implied from the method that is called,
|
||||
// or if it uses the state index type on hardware.
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 8-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer8BeginEndInstanceFirst(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 16-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer16BeginEndInstanceFirst(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 32-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer32BeginEndInstanceFirst(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 8-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer8BeginEndInstanceSubsequent(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 16-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer16BeginEndInstanceSubsequent(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 32-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawIndexBuffer32BeginEndInstanceSubsequent(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawIndexBufferBeginEndInstance(engine, argument, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with a low number of index buffer elements,
|
||||
/// while optionally also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
|
||||
private void DrawIndexBufferBeginEndInstance(ThreedClass engine, int argument, bool instanced)
|
||||
{
|
||||
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
|
||||
|
||||
int firstIndex = argument & 0xffff;
|
||||
int indexCount = (argument >> 16) & 0xfff;
|
||||
|
||||
bool oldDrawIndexed = _drawState.DrawIndexed;
|
||||
|
||||
_drawState.DrawIndexed = true;
|
||||
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
|
||||
|
||||
DrawEnd(engine, firstIndex, indexCount, 0, 0);
|
||||
|
||||
_drawState.DrawIndexed = oldDrawIndexed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a non-indexed draw with the specified topology, index and count.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawVertexArrayBeginEndInstanceFirst(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawVertexArrayBeginEndInstance(engine, argument, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a non-indexed draw with the specified topology, index and count,
|
||||
/// while incrementing the current instance.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawVertexArrayBeginEndInstanceSubsequent(ThreedClass engine, int argument)
|
||||
{
|
||||
DrawVertexArrayBeginEndInstance(engine, argument, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with a low number of index buffer elements,
|
||||
/// while optionally also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
|
||||
private void DrawVertexArrayBeginEndInstance(ThreedClass engine, int argument, bool instanced)
|
||||
{
|
||||
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
|
||||
|
||||
int firstVertex = argument & 0xffff;
|
||||
int vertexCount = (argument >> 16) & 0xfff;
|
||||
|
||||
bool oldDrawIndexed = _drawState.DrawIndexed;
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
|
||||
|
||||
DrawEnd(engine, 0, 0, firstVertex, vertexCount);
|
||||
|
||||
_drawState.DrawIndexed = oldDrawIndexed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a texture draw with a source texture and sampler ID, along with source
|
||||
/// and destination coordinates and sizes.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void DrawTexture(ThreedClass engine, int argument)
|
||||
{
|
||||
static float FixedToFloat(int fixedValue)
|
||||
{
|
||||
return fixedValue * (1f / 4096);
|
||||
}
|
||||
|
||||
float dstX0 = FixedToFloat(_state.State.DrawTextureDstX);
|
||||
float dstY0 = FixedToFloat(_state.State.DrawTextureDstY);
|
||||
float dstWidth = FixedToFloat(_state.State.DrawTextureDstWidth);
|
||||
float dstHeight = FixedToFloat(_state.State.DrawTextureDstHeight);
|
||||
|
||||
// TODO: Confirm behaviour on hardware.
|
||||
// When this is active, the origin appears to be on the bottom.
|
||||
if (_state.State.YControl.HasFlag(YControl.NegateY))
|
||||
{
|
||||
dstY0 -= dstHeight;
|
||||
}
|
||||
|
||||
float dstX1 = dstX0 + dstWidth;
|
||||
float dstY1 = dstY0 + dstHeight;
|
||||
|
||||
float srcX0 = FixedToFloat(_state.State.DrawTextureSrcX);
|
||||
float srcY0 = FixedToFloat(_state.State.DrawTextureSrcY);
|
||||
float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
|
||||
float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
|
||||
|
||||
engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
|
||||
|
||||
_channel.TextureManager.UpdateRenderTargets();
|
||||
|
||||
int textureId = _state.State.DrawTextureTextureId;
|
||||
int samplerId = _state.State.DrawTextureSamplerId;
|
||||
|
||||
(var texture, var sampler) = _channel.TextureManager.GetGraphicsTextureAndSampler(textureId, samplerId);
|
||||
|
||||
srcX0 *= texture.ScaleFactor;
|
||||
srcY0 *= texture.ScaleFactor;
|
||||
srcX1 *= texture.ScaleFactor;
|
||||
srcY1 *= texture.ScaleFactor;
|
||||
|
||||
float dstScale = _channel.TextureManager.RenderTargetScale;
|
||||
|
||||
dstX0 *= dstScale;
|
||||
dstY0 *= dstScale;
|
||||
dstX1 *= dstScale;
|
||||
dstY1 *= dstScale;
|
||||
|
||||
_context.Renderer.Pipeline.DrawTexture(
|
||||
texture?.HostTexture,
|
||||
sampler?.GetHostSampler(texture),
|
||||
new Extents2DF(srcX0, srcY0, srcX1, srcY1),
|
||||
new Extents2DF(dstX0, dstY0, dstX1, dstY1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed or non-indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
|
||||
/// <param name="instanceCount">Instance count</param>
|
||||
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
|
||||
/// <param name="firstVertex">First vertex on the vertex buffer</param>
|
||||
/// <param name="firstInstance">First instance</param>
|
||||
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
|
||||
public void Draw(
|
||||
ThreedClass engine,
|
||||
PrimitiveTopology topology,
|
||||
int count,
|
||||
int instanceCount,
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance,
|
||||
bool indexed)
|
||||
{
|
||||
UpdateTopology(topology);
|
||||
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
_channel.MemoryManager,
|
||||
_state.State.RenderEnableAddress,
|
||||
_state.State.RenderEnableCondition);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.False)
|
||||
{
|
||||
_drawState.DrawIndexed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_drawState.FirstIndex = firstIndex;
|
||||
_drawState.IndexCount = count;
|
||||
_state.State.FirstVertex = (uint)firstVertex;
|
||||
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
_drawState.DrawFirstVertex = firstVertex;
|
||||
_drawState.DrawVertexCount = count;
|
||||
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
|
||||
}
|
||||
|
||||
_state.State.FirstInstance = (uint)firstInstance;
|
||||
|
||||
_drawState.DrawIndexed = indexed;
|
||||
_currentSpecState.SetHasConstantBufferDrawParameters(true);
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
|
||||
_state.State.FirstVertex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
|
||||
}
|
||||
|
||||
_state.State.FirstInstance = 0;
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
|
||||
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
|
||||
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||
public void DrawIndirect(
|
||||
ThreedClass engine,
|
||||
PrimitiveTopology topology,
|
||||
ulong indirectBufferAddress,
|
||||
ulong parameterBufferAddress,
|
||||
int maxDrawCount,
|
||||
int stride,
|
||||
int indexCount,
|
||||
IndirectDrawType drawType)
|
||||
{
|
||||
UpdateTopology(topology);
|
||||
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
_channel.MemoryManager,
|
||||
_state.State.RenderEnableAddress,
|
||||
_state.State.RenderEnableCondition);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.False)
|
||||
{
|
||||
_drawState.DrawIndexed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PhysicalMemory memory = _channel.MemoryManager.Physical;
|
||||
|
||||
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
||||
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount);
|
||||
_drawState.FirstIndex = 0;
|
||||
_drawState.IndexCount = indexCount;
|
||||
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = indexed;
|
||||
_drawState.DrawIndirect = true;
|
||||
_currentSpecState.SetHasConstantBufferDrawParameters(true);
|
||||
|
||||
engine.UpdateState();
|
||||
|
||||
if (hasCount)
|
||||
{
|
||||
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride);
|
||||
var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4);
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride);
|
||||
|
||||
if (indexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndirect(indirectBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
_drawState.DrawIndexed = false;
|
||||
_drawState.DrawIndirect = false;
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any deferred draws.
|
||||
/// This is used for instanced draws.
|
||||
/// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
|
||||
/// Once we detect the last instanced draw, then we perform the host instanced draw,
|
||||
/// with the accumulated instance count.
|
||||
/// </summary>
|
||||
public void PerformDeferredDraws()
|
||||
{
|
||||
// Perform any pending instanced draw.
|
||||
if (_instancedDrawPending)
|
||||
{
|
||||
_instancedDrawPending = false;
|
||||
|
||||
bool indexedInline = _instancedIndexedInline;
|
||||
|
||||
if (_instancedIndexed || indexedInline)
|
||||
{
|
||||
if (indexedInline)
|
||||
{
|
||||
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
|
||||
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
|
||||
|
||||
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.DrawIndexed(
|
||||
_instancedIndexCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedFirstIndex,
|
||||
_instancedFirstVertex,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.Draw(
|
||||
_instancedDrawStateCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedDrawStateFirst,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also be specified with the argument.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void Clear(ThreedClass engine, int argument)
|
||||
{
|
||||
Clear(engine, argument, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also specified with the arguments.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
|
||||
public void Clear(ThreedClass engine, int argument, int layerCount)
|
||||
{
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
_channel.MemoryManager,
|
||||
_state.State.RenderEnableAddress,
|
||||
_state.State.RenderEnableCondition);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.False)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool clearDepth = (argument & 1) != 0;
|
||||
bool clearStencil = (argument & 2) != 0;
|
||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||
int index = (argument >> 6) & 0xf;
|
||||
int layer = (argument >> 10) & 0x3ff;
|
||||
|
||||
RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
|
||||
|
||||
if (layer != 0 || layerCount > 1)
|
||||
{
|
||||
updateFlags |= RenderTargetUpdateFlags.Layered;
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
{
|
||||
updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil;
|
||||
}
|
||||
|
||||
engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1);
|
||||
|
||||
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
||||
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
||||
// host clipping.
|
||||
var screenScissorState = _state.State.ScreenScissorState;
|
||||
|
||||
// Must happen after UpdateRenderTargetState to have up-to-date clip region values.
|
||||
bool clipMismatch = (screenScissorState.X | screenScissorState.Y) != 0 ||
|
||||
screenScissorState.Width != _channel.TextureManager.ClipRegionWidth ||
|
||||
screenScissorState.Height != _channel.TextureManager.ClipRegionHeight;
|
||||
|
||||
bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0;
|
||||
bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0;
|
||||
bool needsCustomScissor = !clearAffectedByScissor || clipMismatch;
|
||||
|
||||
// Scissor and rasterizer discard also affect clears.
|
||||
ulong updateMask = 1UL << StateUpdater.RasterizerStateIndex;
|
||||
|
||||
if (!needsCustomScissor)
|
||||
{
|
||||
updateMask |= 1UL << StateUpdater.ScissorStateIndex;
|
||||
}
|
||||
|
||||
engine.UpdateState(updateMask);
|
||||
|
||||
if (needsCustomScissor)
|
||||
{
|
||||
int scissorX = screenScissorState.X;
|
||||
int scissorY = screenScissorState.Y;
|
||||
int scissorW = screenScissorState.Width;
|
||||
int scissorH = screenScissorState.Height;
|
||||
|
||||
if (clearAffectedByScissor && _state.State.ScissorState[0].Enable)
|
||||
{
|
||||
ref var scissorState = ref _state.State.ScissorState[0];
|
||||
|
||||
scissorX = Math.Max(scissorX, scissorState.X1);
|
||||
scissorY = Math.Max(scissorY, scissorState.Y1);
|
||||
scissorW = Math.Min(scissorW, scissorState.X2 - scissorState.X1);
|
||||
scissorH = Math.Min(scissorH, scissorState.Y2 - scissorState.Y1);
|
||||
}
|
||||
|
||||
float scale = _channel.TextureManager.RenderTargetScale;
|
||||
if (scale != 1f)
|
||||
{
|
||||
scissorX = (int)(scissorX * scale);
|
||||
scissorY = (int)(scissorY * scale);
|
||||
scissorW = (int)MathF.Ceiling(scissorW * scale);
|
||||
scissorH = (int)MathF.Ceiling(scissorH * scale);
|
||||
}
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[]
|
||||
{
|
||||
new Rectangle<int>(scissorX, scissorY, scissorW, scissorH)
|
||||
};
|
||||
|
||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||
}
|
||||
|
||||
_channel.TextureManager.UpdateRenderTargets();
|
||||
|
||||
if (componentMask != 0)
|
||||
{
|
||||
var clearColor = _state.State.ClearColors;
|
||||
|
||||
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, layerCount, componentMask, color);
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
{
|
||||
float depthValue = _state.State.ClearDepthValue;
|
||||
int stencilValue = (int)_state.State.ClearStencilValue;
|
||||
|
||||
int stencilMask = 0;
|
||||
|
||||
if (clearStencil)
|
||||
{
|
||||
stencilMask = clearAffectedByStencilMask ? _state.State.StencilTestState.FrontMask : 0xff;
|
||||
}
|
||||
|
||||
if (clipMismatch)
|
||||
{
|
||||
_channel.TextureManager.UpdateRenderTargetDepthStencil();
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||
layer,
|
||||
layerCount,
|
||||
depthValue,
|
||||
clearDepth,
|
||||
stencilValue,
|
||||
stencilMask);
|
||||
}
|
||||
|
||||
if (needsCustomScissor)
|
||||
{
|
||||
engine.UpdateScissorState();
|
||||
}
|
||||
|
||||
engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||
|
||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||
{
|
||||
_context.Renderer.Pipeline.EndHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
Normal file
65
src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw state.
|
||||
/// </summary>
|
||||
class DrawState
|
||||
{
|
||||
/// <summary>
|
||||
/// First index to be used for the draw on the index buffer.
|
||||
/// </summary>
|
||||
public int FirstIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Number of indices to be used for the draw on the index buffer.
|
||||
/// </summary>
|
||||
public int IndexCount;
|
||||
|
||||
/// <summary>
|
||||
/// First vertex used on non-indexed draws. This value is stored somewhere else on indexed draws.
|
||||
/// </summary>
|
||||
public int DrawFirstVertex;
|
||||
|
||||
/// <summary>
|
||||
/// Vertex count used on non-indexed draws. Indexed draws have a index count instead.
|
||||
/// </summary>
|
||||
public int DrawVertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the next draw will be a indexed draw.
|
||||
/// </summary>
|
||||
public bool DrawIndexed;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the next draw will be a indirect draw.
|
||||
/// </summary>
|
||||
public bool DrawIndirect;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if any of the currently used vertex shaders reads the instance ID.
|
||||
/// </summary>
|
||||
public bool VsUsesInstanceId;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if any of the currently used vertex buffers is instanced.
|
||||
/// </summary>
|
||||
public bool IsAnyVbInstanced;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
|
||||
/// </summary>
|
||||
public bool HasConstantBufferDrawParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive topology for the next draw.
|
||||
/// </summary>
|
||||
public PrimitiveTopology Topology;
|
||||
|
||||
/// <summary>
|
||||
/// Index buffer data streamer for inline index buffer updates, such as those used in legacy OpenGL.
|
||||
/// </summary>
|
||||
public IbStreamer IbStreamer = new IbStreamer();
|
||||
}
|
||||
}
|
194
src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
Normal file
194
src/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
Normal file
|
@ -0,0 +1,194 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds inline index buffer state.
|
||||
/// The inline index buffer data is sent to the GPU through the command buffer.
|
||||
/// </summary>
|
||||
struct IbStreamer
|
||||
{
|
||||
private const int BufferCapacity = 256; // Must be a power of 2.
|
||||
|
||||
private BufferHandle _inlineIndexBuffer;
|
||||
private int _inlineIndexBufferSize;
|
||||
private int _inlineIndexCount;
|
||||
private uint[] _buffer;
|
||||
private int _bufferOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if any index buffer data has been pushed.
|
||||
/// </summary>
|
||||
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Total numbers of indices that have been pushed.
|
||||
/// </summary>
|
||||
public int InlineIndexCount => _inlineIndexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handle for the host buffer currently holding the inline index buffer data.
|
||||
/// </summary>
|
||||
/// <returns>Host buffer handle</returns>
|
||||
public BufferHandle GetInlineIndexBuffer()
|
||||
{
|
||||
return _inlineIndexBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements on the current inline index buffer,
|
||||
/// while also reseting it to zero for the next draw.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <returns>Inline index bufffer count</returns>
|
||||
public int GetAndResetInlineIndexCount(IRenderer renderer)
|
||||
{
|
||||
UpdateRemaining(renderer);
|
||||
int temp = _inlineIndexCount;
|
||||
_inlineIndexCount = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes four 8-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU8(IRenderer renderer, int argument)
|
||||
{
|
||||
byte i0 = (byte)argument;
|
||||
byte i1 = (byte)(argument >> 8);
|
||||
byte i2 = (byte)(argument >> 16);
|
||||
byte i3 = (byte)(argument >> 24);
|
||||
|
||||
int offset = _inlineIndexCount;
|
||||
|
||||
PushData(renderer, offset, i0);
|
||||
PushData(renderer, offset + 1, i1);
|
||||
PushData(renderer, offset + 2, i2);
|
||||
PushData(renderer, offset + 3, i3);
|
||||
|
||||
_inlineIndexCount += 4;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes two 16-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU16(IRenderer renderer, int argument)
|
||||
{
|
||||
ushort i0 = (ushort)argument;
|
||||
ushort i1 = (ushort)(argument >> 16);
|
||||
|
||||
int offset = _inlineIndexCount;
|
||||
|
||||
PushData(renderer, offset, i0);
|
||||
PushData(renderer, offset + 1, i1);
|
||||
|
||||
_inlineIndexCount += 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes one 32-bit index buffer element.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void VbElementU32(IRenderer renderer, int argument)
|
||||
{
|
||||
uint i0 = (uint)argument;
|
||||
|
||||
int offset = _inlineIndexCount++;
|
||||
|
||||
PushData(renderer, offset, i0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a 32-bit value to the index buffer.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <param name="offset">Offset where the data should be written, in 32-bit words</param>
|
||||
/// <param name="value">Index value to be written</param>
|
||||
private void PushData(IRenderer renderer, int offset, uint value)
|
||||
{
|
||||
if (_buffer == null)
|
||||
{
|
||||
_buffer = new uint[BufferCapacity];
|
||||
}
|
||||
|
||||
// We upload data in chunks.
|
||||
// If we are at the start of a chunk, then the buffer might be full,
|
||||
// in that case we need to submit any existing data before overwriting the buffer.
|
||||
int subOffset = offset & (BufferCapacity - 1);
|
||||
|
||||
if (subOffset == 0 && offset != 0)
|
||||
{
|
||||
int baseOffset = (offset - BufferCapacity) * sizeof(uint);
|
||||
BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, BufferCapacity * sizeof(uint));
|
||||
renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer));
|
||||
}
|
||||
|
||||
_buffer[subOffset] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure that any pending data is submitted to the GPU before the index buffer is used.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
private void UpdateRemaining(IRenderer renderer)
|
||||
{
|
||||
int offset = _inlineIndexCount;
|
||||
if (offset == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = offset & (BufferCapacity - 1);
|
||||
if (count == 0)
|
||||
{
|
||||
count = BufferCapacity;
|
||||
}
|
||||
|
||||
int baseOffset = (offset - count) * sizeof(uint);
|
||||
int length = count * sizeof(uint);
|
||||
BufferHandle buffer = GetInlineIndexBuffer(renderer, baseOffset, length);
|
||||
renderer.SetBufferData(buffer, baseOffset, MemoryMarshal.Cast<uint, byte>(_buffer).Slice(0, length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
/// <param name="offset">Offset where the data will be written</param>
|
||||
/// <param name="length">Number of bytes that will be written</param>
|
||||
/// <returns>Buffer handle</returns>
|
||||
private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset, int length)
|
||||
{
|
||||
// Calculate a reasonable size for the buffer that can fit all the data,
|
||||
// and that also won't require frequent resizes if we need to push more data.
|
||||
int size = BitUtils.AlignUp(offset + length + 0x10, 0x200);
|
||||
|
||||
if (_inlineIndexBuffer == BufferHandle.Null)
|
||||
{
|
||||
_inlineIndexBuffer = renderer.CreateBuffer(size);
|
||||
_inlineIndexBufferSize = size;
|
||||
}
|
||||
else if (_inlineIndexBufferSize < size)
|
||||
{
|
||||
BufferHandle oldBuffer = _inlineIndexBuffer;
|
||||
int oldSize = _inlineIndexBufferSize;
|
||||
|
||||
_inlineIndexBuffer = renderer.CreateBuffer(size);
|
||||
_inlineIndexBufferSize = size;
|
||||
|
||||
renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
|
||||
renderer.DeleteBuffer(oldBuffer);
|
||||
}
|
||||
|
||||
return _inlineIndexBuffer;
|
||||
}
|
||||
}
|
||||
}
|
38
src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
Normal file
38
src/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Indirect draw type, which can be indexed or non-indexed, with or without a draw count.
|
||||
/// </summary>
|
||||
enum IndirectDrawType
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-indexed draw without draw count.
|
||||
/// </summary>
|
||||
DrawIndirect = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed draw without draw count.
|
||||
/// </summary>
|
||||
DrawIndexedIndirect = Indexed,
|
||||
|
||||
/// <summary>
|
||||
/// Non-indexed draw with draw count.
|
||||
/// </summary>
|
||||
DrawIndirectCount = Count,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed draw with draw count.
|
||||
/// </summary>
|
||||
DrawIndexedIndirectCount = Indexed | Count,
|
||||
|
||||
/// <summary>
|
||||
/// Indexed flag.
|
||||
/// </summary>
|
||||
Indexed = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Draw count flag.
|
||||
/// </summary>
|
||||
Count = 1 << 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags indicating how the render targets should be updated.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
enum RenderTargetUpdateFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// No flags.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Get render target index from the control register.
|
||||
/// </summary>
|
||||
UseControl = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that all render targets are 2D array textures.
|
||||
/// </summary>
|
||||
Layered = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that only a single color target will be used.
|
||||
/// </summary>
|
||||
SingleColor = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the depth-stencil target will be used.
|
||||
/// </summary>
|
||||
UpdateDepthStencil = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Default update flags for draw.
|
||||
/// </summary>
|
||||
UpdateAll = UseControl | UpdateDepthStencil
|
||||
}
|
||||
}
|
190
src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
Normal file
190
src/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Semaphore updater.
|
||||
/// </summary>
|
||||
class SemaphoreUpdater
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU semaphore operation.
|
||||
/// </summary>
|
||||
private enum SemaphoreOperation
|
||||
{
|
||||
Release = 0,
|
||||
Acquire = 1,
|
||||
Counter = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counter type for GPU counter reset.
|
||||
/// </summary>
|
||||
private enum ResetCounterType
|
||||
{
|
||||
SamplesPassed = 1,
|
||||
ZcullStats = 2,
|
||||
TransformFeedbackPrimitivesWritten = 0x10,
|
||||
InputVertices = 0x12,
|
||||
InputPrimitives = 0x13,
|
||||
VertexShaderInvocations = 0x15,
|
||||
TessControlShaderInvocations = 0x16,
|
||||
TessEvaluationShaderInvocations = 0x17,
|
||||
TessEvaluationShaderPrimitives = 0x18,
|
||||
GeometryShaderInvocations = 0x1a,
|
||||
GeometryShaderPrimitives = 0x1b,
|
||||
ClipperInputPrimitives = 0x1c,
|
||||
ClipperOutputPrimitives = 0x1d,
|
||||
FragmentShaderInvocations = 0x1e,
|
||||
PrimitivesGenerated = 0x1f
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counter type for GPU counter reporting.
|
||||
/// </summary>
|
||||
private enum ReportCounterType
|
||||
{
|
||||
Payload = 0,
|
||||
InputVertices = 1,
|
||||
InputPrimitives = 3,
|
||||
VertexShaderInvocations = 5,
|
||||
GeometryShaderInvocations = 7,
|
||||
GeometryShaderPrimitives = 9,
|
||||
ZcullStats0 = 0xa,
|
||||
TransformFeedbackPrimitivesWritten = 0xb,
|
||||
ZcullStats1 = 0xc,
|
||||
ZcullStats2 = 0xe,
|
||||
ClipperInputPrimitives = 0xf,
|
||||
ZcullStats3 = 0x10,
|
||||
ClipperOutputPrimitives = 0x11,
|
||||
PrimitivesGenerated = 0x12,
|
||||
FragmentShaderInvocations = 0x13,
|
||||
SamplesPassed = 0x15,
|
||||
TransformFeedbackOffset = 0x1a,
|
||||
TessControlShaderInvocations = 0x1b,
|
||||
TessEvaluationShaderInvocations = 0x1d,
|
||||
TessEvaluationShaderPrimitives = 0x1f
|
||||
}
|
||||
|
||||
private readonly GpuContext _context;
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the semaphore updater.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Channel state</param>
|
||||
public SemaphoreUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
|
||||
{
|
||||
_context = context;
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value of an internal GPU counter back to zero.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void ResetCounter(int argument)
|
||||
{
|
||||
ResetCounterType type = (ResetCounterType)argument;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ResetCounterType.SamplesPassed:
|
||||
_context.Renderer.ResetCounter(CounterType.SamplesPassed);
|
||||
break;
|
||||
case ResetCounterType.PrimitivesGenerated:
|
||||
_context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
|
||||
break;
|
||||
case ResetCounterType.TransformFeedbackPrimitivesWritten:
|
||||
_context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU counter to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void Report(int argument)
|
||||
{
|
||||
SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
|
||||
ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case SemaphoreOperation.Release: ReleaseSemaphore(); break;
|
||||
case SemaphoreOperation.Counter: ReportCounter(type); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes (or Releases) a GPU semaphore value to guest memory.
|
||||
/// </summary>
|
||||
private void ReleaseSemaphore()
|
||||
{
|
||||
_channel.MemoryManager.Write(_state.State.SemaphoreAddress.Pack(), _state.State.SemaphorePayload);
|
||||
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packed GPU counter data (including GPU timestamp) in memory.
|
||||
/// </summary>
|
||||
private struct CounterData
|
||||
{
|
||||
public ulong Counter;
|
||||
public ulong Timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU counter to guest memory.
|
||||
/// This also writes the current timestamp value.
|
||||
/// </summary>
|
||||
/// <param name="type">Counter to be written to memory</param>
|
||||
private void ReportCounter(ReportCounterType type)
|
||||
{
|
||||
ulong gpuVa = _state.State.SemaphoreAddress.Pack();
|
||||
|
||||
ulong ticks = _context.GetTimestamp();
|
||||
|
||||
ICounterEvent counter = null;
|
||||
|
||||
void resultHandler(object evt, ulong result)
|
||||
{
|
||||
CounterData counterData = new CounterData
|
||||
{
|
||||
Counter = result,
|
||||
Timestamp = ticks
|
||||
};
|
||||
|
||||
if (counter?.Invalid != true)
|
||||
{
|
||||
_channel.MemoryManager.Write(gpuVa, counterData);
|
||||
}
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ReportCounterType.Payload:
|
||||
resultHandler(null, (ulong)_state.State.SemaphorePayload);
|
||||
break;
|
||||
case ReportCounterType.SamplesPassed:
|
||||
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false);
|
||||
break;
|
||||
case ReportCounterType.PrimitivesGenerated:
|
||||
counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, false);
|
||||
break;
|
||||
case ReportCounterType.TransformFeedbackPrimitivesWritten:
|
||||
counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, false);
|
||||
break;
|
||||
}
|
||||
|
||||
_channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Maintains a "current" specialiation state, and provides a flag to check if it has changed meaningfully.
|
||||
/// </summary>
|
||||
internal class SpecializationStateUpdater
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
private GpuChannelGraphicsState _graphics;
|
||||
private GpuChannelPoolState _pool;
|
||||
|
||||
private bool _usesDrawParameters;
|
||||
private bool _usesTopology;
|
||||
|
||||
private bool _changed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the specialization state updater class.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
public SpecializationStateUpdater(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal that the specialization state has changed.
|
||||
/// </summary>
|
||||
private void Signal()
|
||||
{
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specialization state has changed since the last check.
|
||||
/// </summary>
|
||||
/// <returns>True if it has changed, false otherwise</returns>
|
||||
public bool HasChanged()
|
||||
{
|
||||
if (_changed)
|
||||
{
|
||||
_changed = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active shader, clearing the dirty state and recording if certain specializations are noteworthy.
|
||||
/// </summary>
|
||||
/// <param name="gs">The active shader</param>
|
||||
public void SetShader(CachedShaderProgram gs)
|
||||
{
|
||||
_usesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
|
||||
_usesTopology = gs.SpecializationState.IsPrimitiveTopologyQueried();
|
||||
|
||||
_changed = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current graphics state.
|
||||
/// </summary>
|
||||
/// <returns>GPU graphics state</returns>
|
||||
public ref GpuChannelGraphicsState GetGraphicsState()
|
||||
{
|
||||
return ref _graphics;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current pool state.
|
||||
/// </summary>
|
||||
/// <returns>GPU pool state</returns>
|
||||
public ref GpuChannelPoolState GetPoolState()
|
||||
{
|
||||
return ref _pool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Early Z force enable.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetEarlyZForce(bool value)
|
||||
{
|
||||
_graphics.EarlyZForce = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primitive topology of current draw.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetTopology(PrimitiveTopology value)
|
||||
{
|
||||
if (value != _graphics.Topology)
|
||||
{
|
||||
_graphics.Topology = value;
|
||||
|
||||
if (_usesTopology)
|
||||
{
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tessellation mode.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetTessellationMode(TessMode value)
|
||||
{
|
||||
if (value.Packed != _graphics.TessellationMode.Packed)
|
||||
{
|
||||
_graphics.TessellationMode = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates alpha-to-coverage state, and sets it as changed.
|
||||
/// </summary>
|
||||
/// <param name="enable">Whether alpha-to-coverage is enabled</param>
|
||||
/// <param name="ditherEnable">Whether alpha-to-coverage dithering is enabled</param>
|
||||
public void SetAlphaToCoverageEnable(bool enable, bool ditherEnable)
|
||||
{
|
||||
_graphics.AlphaToCoverageEnable = enable;
|
||||
_graphics.AlphaToCoverageDitherEnable = ditherEnable;
|
||||
|
||||
Signal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the viewport transform is disabled.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetViewportTransformDisable(bool value)
|
||||
{
|
||||
if (value != _graphics.ViewportTransformDisable)
|
||||
{
|
||||
_graphics.ViewportTransformDisable = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Depth mode zero to one or minus one to one.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetDepthMode(bool value)
|
||||
{
|
||||
if (value != _graphics.DepthMode)
|
||||
{
|
||||
_graphics.DepthMode = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the point size is set on the shader or is fixed.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetProgramPointSizeEnable(bool value)
|
||||
{
|
||||
if (value != _graphics.ProgramPointSizeEnable)
|
||||
{
|
||||
_graphics.ProgramPointSizeEnable = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Point size used if <see cref="SetProgramPointSizeEnable" /> is provided false.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetPointSize(float value)
|
||||
{
|
||||
if (value != _graphics.PointSize)
|
||||
{
|
||||
_graphics.PointSize = value;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates alpha test specialization state, and sets it as changed.
|
||||
/// </summary>
|
||||
/// <param name="enable">Whether alpha test is enabled</param>
|
||||
/// <param name="reference">The value to compare with the fragment output alpha</param>
|
||||
/// <param name="op">The comparison that decides if the fragment should be discarded</param>
|
||||
public void SetAlphaTest(bool enable, float reference, CompareOp op)
|
||||
{
|
||||
_graphics.AlphaTestEnable = enable;
|
||||
_graphics.AlphaTestReference = reference;
|
||||
_graphics.AlphaTestCompare = op;
|
||||
|
||||
Signal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the type of the vertex attributes consumed by the shader.
|
||||
/// </summary>
|
||||
/// <param name="state">The new state</param>
|
||||
public void SetAttributeTypes(ref Array32<VertexAttribState> state)
|
||||
{
|
||||
bool changed = false;
|
||||
ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
|
||||
|
||||
for (int location = 0; location < state.Length; location++)
|
||||
{
|
||||
VertexAttribType type = state[location].UnpackType();
|
||||
|
||||
AttributeType value = type switch
|
||||
{
|
||||
VertexAttribType.Sint => AttributeType.Sint,
|
||||
VertexAttribType.Uint => AttributeType.Uint,
|
||||
_ => AttributeType.Float
|
||||
};
|
||||
|
||||
if (attributeTypes[location] != value)
|
||||
{
|
||||
attributeTypes[location] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the type of the outputs produced by the fragment shader based on the current render target state.
|
||||
/// </summary>
|
||||
/// <param name="rtControl">The render target control register</param>
|
||||
/// <param name="state">The color attachment state</param>
|
||||
public void SetFragmentOutputTypes(RtControl rtControl, ref Array8<RtColorState> state)
|
||||
{
|
||||
bool changed = false;
|
||||
int count = rtControl.UnpackCount();
|
||||
|
||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
int rtIndex = rtControl.UnpackPermutationIndex(index);
|
||||
|
||||
var colorState = state[rtIndex];
|
||||
|
||||
if (index < count && StateUpdater.IsRtEnabled(colorState))
|
||||
{
|
||||
Format format = colorState.Format.Convert().Format;
|
||||
|
||||
AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;
|
||||
|
||||
if (type != _graphics.FragmentOutputTypes[index])
|
||||
{
|
||||
_graphics.FragmentOutputTypes[index] = type;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization)
|
||||
{
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
public void SetHasConstantBufferDrawParameters(bool value)
|
||||
{
|
||||
if (value != _graphics.HasConstantBufferDrawParameters)
|
||||
{
|
||||
_graphics.HasConstantBufferDrawParameters = value;
|
||||
|
||||
if (_usesDrawParameters)
|
||||
{
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that any storage buffer use is unaligned.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value</param>
|
||||
/// <returns>True if the unaligned state changed, false otherwise</returns>
|
||||
public bool SetHasUnalignedStorageBuffer(bool value)
|
||||
{
|
||||
if (value != _graphics.HasUnalignedStorageBuffer)
|
||||
{
|
||||
_graphics.HasUnalignedStorageBuffer = value;
|
||||
|
||||
Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the GPU pool state.
|
||||
/// </summary>
|
||||
/// <param name="state">The new state</param>
|
||||
public void SetPoolState(GpuChannelPoolState state)
|
||||
{
|
||||
if (!state.Equals(_pool))
|
||||
{
|
||||
_pool = state;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the dual-source blend enabled state.
|
||||
/// </summary>
|
||||
/// <param name="enabled">True if blending is enabled and using dual-source blend</param>
|
||||
public void SetDualSourceBlendEnabled(bool enabled)
|
||||
{
|
||||
if (enabled != _graphics.DualSourceBlendEnable)
|
||||
{
|
||||
_graphics.DualSourceBlendEnable = enabled;
|
||||
|
||||
Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
177
src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
Normal file
177
src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
using Ryujinx.Graphics.Device;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// State update callback entry, with the callback function and associated field names.
|
||||
/// </summary>
|
||||
readonly struct StateUpdateCallbackEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback function, to be called if the register was written as the state needs to be updated.
|
||||
/// </summary>
|
||||
public Action Callback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the state fields (registers) associated with the callback function.
|
||||
/// </summary>
|
||||
public string[] FieldNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new state update callback entry.
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback function, to be called if the register was written as the state needs to be updated</param>
|
||||
/// <param name="fieldNames">Name of the state fields (registers) associated with the callback function</param>
|
||||
public StateUpdateCallbackEntry(Action callback, params string[] fieldNames)
|
||||
{
|
||||
Callback = callback;
|
||||
FieldNames = fieldNames;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GPU state update tracker.
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">State type</typeparam>
|
||||
class StateUpdateTracker<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TState>
|
||||
{
|
||||
private const int BlockSize = 0xe00;
|
||||
private const int RegisterSize = sizeof(uint);
|
||||
|
||||
private readonly byte[] _registerToGroupMapping;
|
||||
private readonly Action[] _callbacks;
|
||||
private ulong _dirtyMask;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the state update tracker.
|
||||
/// </summary>
|
||||
/// <param name="entries">Update tracker callback entries</param>
|
||||
public StateUpdateTracker(StateUpdateCallbackEntry[] entries)
|
||||
{
|
||||
_registerToGroupMapping = new byte[BlockSize];
|
||||
_callbacks = new Action[entries.Length];
|
||||
|
||||
var fieldToDelegate = new Dictionary<string, int>();
|
||||
|
||||
for (int entryIndex = 0; entryIndex < entries.Length; entryIndex++)
|
||||
{
|
||||
var entry = entries[entryIndex];
|
||||
|
||||
foreach (var fieldName in entry.FieldNames)
|
||||
{
|
||||
fieldToDelegate.Add(fieldName, entryIndex);
|
||||
}
|
||||
|
||||
_callbacks[entryIndex] = entry.Callback;
|
||||
}
|
||||
|
||||
var fields = typeof(TState).GetFields();
|
||||
int offset = 0;
|
||||
|
||||
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
|
||||
{
|
||||
var field = fields[fieldIndex];
|
||||
|
||||
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
||||
|
||||
if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
|
||||
{
|
||||
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
||||
{
|
||||
_registerToGroupMapping[(offset + i) / RegisterSize] = (byte)(entryIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
offset += sizeOfField;
|
||||
}
|
||||
|
||||
Debug.Assert(offset == Unsafe.SizeOf<TState>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a register as modified.
|
||||
/// </summary>
|
||||
/// <param name="offset">Register offset in bytes</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetDirty(int offset)
|
||||
{
|
||||
uint index = (uint)offset / RegisterSize;
|
||||
|
||||
if (index < BlockSize)
|
||||
{
|
||||
int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (IntPtr)index);
|
||||
if (groupIndex != 0)
|
||||
{
|
||||
groupIndex--;
|
||||
_dirtyMask |= 1UL << groupIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces a register group as dirty, by index.
|
||||
/// </summary>
|
||||
/// <param name="groupIndex">Index of the group to be dirtied</param>
|
||||
public void ForceDirty(int groupIndex)
|
||||
{
|
||||
if ((uint)groupIndex >= _callbacks.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(groupIndex));
|
||||
}
|
||||
|
||||
_dirtyMask |= 1UL << groupIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces all register groups as dirty, triggering a full update on the next call to <see cref="Update"/>.
|
||||
/// </summary>
|
||||
public void SetAllDirty()
|
||||
{
|
||||
Debug.Assert(_callbacks.Length <= sizeof(ulong) * 8);
|
||||
_dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given register group is dirty without clearing it.
|
||||
/// </summary>
|
||||
/// <param name="groupIndex">Index of the group to check</param>
|
||||
/// <returns>True if dirty, false otherwise</returns>
|
||||
public bool IsDirty(int groupIndex)
|
||||
{
|
||||
return (_dirtyMask & (1UL << groupIndex)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check all the groups specified by <paramref name="checkMask"/> for modification, and update if modified.
|
||||
/// </summary>
|
||||
/// <param name="checkMask">Mask, where each bit set corresponds to a group index that should be checked</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Update(ulong checkMask)
|
||||
{
|
||||
ulong mask = _dirtyMask & checkMask;
|
||||
if (mask == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
int groupIndex = BitOperations.TrailingZeroCount(mask);
|
||||
|
||||
_callbacks[groupIndex]();
|
||||
|
||||
mask &= ~(1UL << groupIndex);
|
||||
}
|
||||
while (mask != 0);
|
||||
|
||||
_dirtyMask &= ~checkMask;
|
||||
}
|
||||
}
|
||||
}
|
1448
src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
Normal file
1448
src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
Normal file
File diff suppressed because it is too large
Load diff
620
src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
Normal file
620
src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
Normal file
|
@ -0,0 +1,620 @@
|
|||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a 3D engine class.
|
||||
/// </summary>
|
||||
class ThreedClass : IDeviceState
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
private readonly GPFifoClass _fifoClass;
|
||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||
|
||||
private readonly InlineToMemoryClass _i2mClass;
|
||||
private readonly AdvancedBlendManager _blendManager;
|
||||
private readonly DrawManager _drawManager;
|
||||
private readonly SemaphoreUpdater _semaphoreUpdater;
|
||||
private readonly ConstantBufferUpdater _cbUpdater;
|
||||
private readonly StateUpdater _stateUpdater;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the 3D engine class.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
public ThreedClass(GpuContext context, GpuChannel channel, GPFifoClass fifoClass)
|
||||
{
|
||||
_context = context;
|
||||
_fifoClass = fifoClass;
|
||||
_state = new DeviceStateWithShadow<ThreedClassState>(new Dictionary<string, RwCallback>
|
||||
{
|
||||
{ nameof(ThreedClassState.LaunchDma), new RwCallback(LaunchDma, null) },
|
||||
{ nameof(ThreedClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
|
||||
{ nameof(ThreedClassState.SyncpointAction), new RwCallback(IncrementSyncpoint, null) },
|
||||
{ nameof(ThreedClassState.InvalidateSamplerCacheNoWfi), new RwCallback(InvalidateSamplerCacheNoWfi, null) },
|
||||
{ nameof(ThreedClassState.InvalidateTextureHeaderCacheNoWfi), new RwCallback(InvalidateTextureHeaderCacheNoWfi, null) },
|
||||
{ nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
|
||||
{ nameof(ThreedClassState.LoadBlendUcodeStart), new RwCallback(LoadBlendUcodeStart, null) },
|
||||
{ nameof(ThreedClassState.LoadBlendUcodeInstruction), new RwCallback(LoadBlendUcodeInstruction, null) },
|
||||
{ nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
|
||||
{ nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) },
|
||||
{ nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) },
|
||||
{ nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceSubsequent), new RwCallback(DrawVertexArrayBeginEndInstanceSubsequent, null) },
|
||||
{ nameof(ThreedClassState.VbElementU8), new RwCallback(VbElementU8, null) },
|
||||
{ nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
|
||||
{ nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, null) },
|
||||
{ nameof(ThreedClassState.ResetCounter), new RwCallback(ResetCounter, null) },
|
||||
{ nameof(ThreedClassState.RenderEnableCondition), new RwCallback(null, Zero) },
|
||||
{ nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
|
||||
{ nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer32BeginEndInstanceFirst, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer16BeginEndInstanceFirst, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer8BeginEndInstanceFirst, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer32BeginEndInstanceSubsequent, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer16BeginEndInstanceSubsequent, null) },
|
||||
{ nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer8BeginEndInstanceSubsequent, null) },
|
||||
{ nameof(ThreedClassState.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
|
||||
{ nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
|
||||
{ nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
|
||||
{ nameof(ThreedClassState.SetFalcon04), new RwCallback(SetFalcon04, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferUpdateData), new RwCallback(ConstantBufferUpdate, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferBindVertex), new RwCallback(ConstantBufferBindVertex, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferBindTessControl), new RwCallback(ConstantBufferBindTessControl, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferBindTessEvaluation), new RwCallback(ConstantBufferBindTessEvaluation, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferBindGeometry), new RwCallback(ConstantBufferBindGeometry, null) },
|
||||
{ nameof(ThreedClassState.UniformBufferBindFragment), new RwCallback(ConstantBufferBindFragment, null) }
|
||||
});
|
||||
|
||||
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
|
||||
|
||||
var spec = new SpecializationStateUpdater(context);
|
||||
var drawState = new DrawState();
|
||||
|
||||
_drawManager = new DrawManager(context, channel, _state, drawState, spec);
|
||||
_blendManager = new AdvancedBlendManager(_state);
|
||||
_semaphoreUpdater = new SemaphoreUpdater(context, channel, _state);
|
||||
_cbUpdater = new ConstantBufferUpdater(channel, _state);
|
||||
_stateUpdater = new StateUpdater(context, channel, _state, drawState, _blendManager, spec);
|
||||
|
||||
// This defaults to "always", even without any register write.
|
||||
// Reads just return 0, regardless of what was set there.
|
||||
_state.State.RenderEnableCondition = Condition.Always;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the class registers.
|
||||
/// </summary>
|
||||
/// <param name="offset">Register byte offset</param>
|
||||
/// <returns>Data at the specified offset</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Read(int offset) => _state.Read(offset);
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the class registers.
|
||||
/// </summary>
|
||||
/// <param name="offset">Register byte offset</param>
|
||||
/// <param name="data">Data to be written</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(int offset, int data)
|
||||
{
|
||||
_state.WriteWithRedundancyCheck(offset, data, out bool valueChanged);
|
||||
|
||||
if (valueChanged)
|
||||
{
|
||||
_stateUpdater.SetDirty(offset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the shadow ram control value of all sub-channels.
|
||||
/// </summary>
|
||||
/// <param name="control">New shadow ram control value</param>
|
||||
public void SetShadowRamControl(int control)
|
||||
{
|
||||
_state.State.SetMmeShadowRamControl = (uint)control;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates current host state for all registers modified since the last call to this method.
|
||||
/// </summary>
|
||||
public void UpdateState()
|
||||
{
|
||||
_fifoClass.CreatePendingSyncs();
|
||||
_cbUpdater.FlushUboDirty();
|
||||
_stateUpdater.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates current host state for all registers modified since the last call to this method.
|
||||
/// </summary>
|
||||
/// <param name="mask">Mask where each bit set indicates that the respective state group index should be checked</param>
|
||||
public void UpdateState(ulong mask)
|
||||
{
|
||||
_stateUpdater.Update(mask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||
/// </summary>
|
||||
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||
{
|
||||
_stateUpdater.UpdateRenderTargetState(updateFlags, singleUse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates scissor based on current render target state.
|
||||
/// </summary>
|
||||
public void UpdateScissorState()
|
||||
{
|
||||
_stateUpdater.UpdateScissorState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the entire state as dirty, forcing a full host state update before the next draw.
|
||||
/// </summary>
|
||||
public void ForceStateDirty()
|
||||
{
|
||||
_drawManager.ForceStateDirty();
|
||||
_stateUpdater.SetAllDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the specified register offset as dirty, forcing the associated state to update on the next draw.
|
||||
/// </summary>
|
||||
/// <param name="offset">Register offset</param>
|
||||
public void ForceStateDirty(int offset)
|
||||
{
|
||||
_stateUpdater.SetDirty(offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces the shaders to be rebound on the next draw.
|
||||
/// </summary>
|
||||
public void ForceShaderUpdate()
|
||||
{
|
||||
_stateUpdater.ForceShaderUpdate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create any syncs from WaitForIdle command that are currently pending.
|
||||
/// </summary>
|
||||
public void CreatePendingSyncs()
|
||||
{
|
||||
_fifoClass.CreatePendingSyncs();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes any queued UBO updates.
|
||||
/// </summary>
|
||||
public void FlushUboDirty()
|
||||
{
|
||||
_cbUpdater.FlushUboDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any deferred draws.
|
||||
/// </summary>
|
||||
public void PerformDeferredDraws()
|
||||
{
|
||||
_drawManager.PerformDeferredDraws();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently bound constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to be written to the buffer</param>
|
||||
public void ConstantBufferUpdate(ReadOnlySpan<int> data)
|
||||
{
|
||||
_cbUpdater.Update(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Inline-to-Memory DMA copy operation.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void LaunchDma(int argument)
|
||||
{
|
||||
_i2mClass.LaunchDma(ref Unsafe.As<ThreedClassState, InlineToMemoryClassState>(ref _state.State), argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a block of data to the Inline-to-Memory engine.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to push</param>
|
||||
public void LoadInlineData(ReadOnlySpan<int> data)
|
||||
{
|
||||
_i2mClass.LoadInlineData(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a word of data to the Inline-to-Memory engine.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void LoadInlineData(int argument)
|
||||
{
|
||||
_i2mClass.LoadInlineData(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an incrementation on a syncpoint.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void IncrementSyncpoint(int argument)
|
||||
{
|
||||
uint syncpointId = (uint)argument & 0xFFFF;
|
||||
|
||||
_context.AdvanceSequence();
|
||||
_context.CreateHostSyncIfNeeded(true, true);
|
||||
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cache with the sampler descriptors from the sampler pool.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void InvalidateSamplerCacheNoWfi(int argument)
|
||||
{
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cache with the texture descriptors from the texture pool.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void InvalidateTextureHeaderCacheNoWfi(int argument)
|
||||
{
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues a texture barrier.
|
||||
/// This waits until previous texture writes from the GPU to finish, before
|
||||
/// performing new operations with said textures.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void TextureBarrier(int argument)
|
||||
{
|
||||
_context.Renderer.Pipeline.TextureBarrier();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the start offset of the blend microcode in memory.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void LoadBlendUcodeStart(int argument)
|
||||
{
|
||||
_blendManager.LoadBlendUcodeStart(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes one word of blend microcode.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void LoadBlendUcodeInstruction(int argument)
|
||||
{
|
||||
_blendManager.LoadBlendUcodeInstruction(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues a texture barrier.
|
||||
/// This waits until previous texture writes from the GPU to finish, before
|
||||
/// performing new operations with said textures.
|
||||
/// This performs a per-tile wait, it is only valid if both the previous write
|
||||
/// and current access has the same access patterns.
|
||||
/// This may be faster than the regular barrier on tile-based rasterizers.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void TextureBarrierTiled(int argument)
|
||||
{
|
||||
_context.Renderer.Pipeline.TextureBarrierTiled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a texture, without needing to specify shader programs.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawTexture(int argument)
|
||||
{
|
||||
_drawManager.DrawTexture(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a non-indexed draw with the specified topology, index and count.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawVertexArrayBeginEndInstanceFirst(int argument)
|
||||
{
|
||||
_drawManager.DrawVertexArrayBeginEndInstanceFirst(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a non-indexed draw with the specified topology, index and count,
|
||||
/// while incrementing the current instance.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawVertexArrayBeginEndInstanceSubsequent(int argument)
|
||||
{
|
||||
_drawManager.DrawVertexArrayBeginEndInstanceSubsequent(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes four 8-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void VbElementU8(int argument)
|
||||
{
|
||||
_drawManager.VbElementU8(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes two 16-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void VbElementU16(int argument)
|
||||
{
|
||||
_drawManager.VbElementU16(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes one 32-bit index buffer element.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void VbElementU32(int argument)
|
||||
{
|
||||
_drawManager.VbElementU32(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value of an internal GPU counter back to zero.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ResetCounter(int argument)
|
||||
{
|
||||
_semaphoreUpdater.ResetCounter(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finishes the draw call.
|
||||
/// This draws geometry on the bound buffers based on the current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawEnd(int argument)
|
||||
{
|
||||
_drawManager.DrawEnd(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts draw.
|
||||
/// This sets primitive type and instanced draw parameters.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawBegin(int argument)
|
||||
{
|
||||
_drawManager.DrawBegin(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index buffer count.
|
||||
/// This also sets internal state that indicates that the next draw is an indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void SetIndexBufferCount(int argument)
|
||||
{
|
||||
_drawManager.SetIndexBufferCount(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 8-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer8BeginEndInstanceFirst(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer8BeginEndInstanceFirst(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 16-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer16BeginEndInstanceFirst(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer16BeginEndInstanceFirst(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 32-bit index buffer elements.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer32BeginEndInstanceFirst(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer32BeginEndInstanceFirst(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 8-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer8BeginEndInstanceSubsequent(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer8BeginEndInstanceSubsequent(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 16-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer16BeginEndInstanceSubsequent(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer16BeginEndInstanceSubsequent(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed draw with 32-bit index buffer elements,
|
||||
/// while also pre-incrementing the current instance value.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawIndexBuffer32BeginEndInstanceSubsequent(int argument)
|
||||
{
|
||||
_drawManager.DrawIndexBuffer32BeginEndInstanceSubsequent(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared is also specified on the argument.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void Clear(int argument)
|
||||
{
|
||||
_drawManager.Clear(this, argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU counter to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void Report(int argument)
|
||||
{
|
||||
_semaphoreUpdater.Report(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs high-level emulation of Falcon microcode function number "4".
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void SetFalcon04(int argument)
|
||||
{
|
||||
_state.State.SetMmeShadowScratch[0] = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the uniform buffer data with inline data.
|
||||
/// </summary>
|
||||
/// <param name="argument">New uniform buffer data word</param>
|
||||
private void ConstantBufferUpdate(int argument)
|
||||
{
|
||||
_cbUpdater.Update(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the vertex shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ConstantBufferBindVertex(int argument)
|
||||
{
|
||||
_cbUpdater.BindVertex(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation control shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ConstantBufferBindTessControl(int argument)
|
||||
{
|
||||
_cbUpdater.BindTessControl(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation evaluation shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ConstantBufferBindTessEvaluation(int argument)
|
||||
{
|
||||
_cbUpdater.BindTessEvaluation(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the geometry shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ConstantBufferBindGeometry(int argument)
|
||||
{
|
||||
_cbUpdater.BindGeometry(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the fragment shader stage.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ConstantBufferBindFragment(int argument)
|
||||
{
|
||||
_cbUpdater.BindFragment(argument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic register read function that just returns 0.
|
||||
/// </summary>
|
||||
/// <returns>Zero</returns>
|
||||
private static int Zero()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indexed or non-indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
|
||||
/// <param name="instanceCount">Instance count</param>
|
||||
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
|
||||
/// <param name="firstVertex">First vertex on the vertex buffer</param>
|
||||
/// <param name="firstInstance">First instance</param>
|
||||
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
|
||||
public void Draw(
|
||||
PrimitiveTopology topology,
|
||||
int count,
|
||||
int instanceCount,
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance,
|
||||
bool indexed)
|
||||
{
|
||||
_drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="indirectBufferAddress">Address of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||
/// <param name="parameterBufferAddress">Address of the buffer with the draw count</param>
|
||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||
/// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
|
||||
/// <param name="indexCount">Maximum number of indices that the draw can consume</param>
|
||||
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||
public void DrawIndirect(
|
||||
PrimitiveTopology topology,
|
||||
ulong indirectBufferAddress,
|
||||
ulong parameterBufferAddress,
|
||||
int maxDrawCount,
|
||||
int stride,
|
||||
int indexCount,
|
||||
IndirectDrawType drawType)
|
||||
{
|
||||
_drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also specified with the arguments.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
|
||||
public void Clear(int argument, int layerCount)
|
||||
{
|
||||
_drawManager.Clear(this, argument, layerCount);
|
||||
}
|
||||
}
|
||||
}
|
1048
src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
Normal file
1048
src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue