NVDEC implementation using FFmpeg (#443)
* Initial nvdec implementation using FFmpeg * Fix swapped channels on the video decoder and the G8R8 texture format * Fix texture samplers not being set properly (regression) * Rebased * Remove unused code introduced on the rebase * Add support for RGBA8 output format on the video image composer * Correct spacing * Some fixes for rebase and other tweaks * Allow size mismatch on frame copy * Get rid of GetHostAddress calls on VDec
This commit is contained in:
11
Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs
Normal file
11
Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Ryujinx.Graphics.Memory;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
interface INvGpuEngine
|
||||
{
|
||||
int[] Registers { get; }
|
||||
|
||||
void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall);
|
||||
}
|
||||
}
|
416
Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs
Normal file
416
Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs
Normal file
@ -0,0 +1,416 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
class MacroInterpreter
|
||||
{
|
||||
private enum AssignmentOperation
|
||||
{
|
||||
IgnoreAndFetch = 0,
|
||||
Move = 1,
|
||||
MoveAndSetMaddr = 2,
|
||||
FetchAndSend = 3,
|
||||
MoveAndSend = 4,
|
||||
FetchAndSetMaddr = 5,
|
||||
MoveAndSetMaddrThenFetchAndSend = 6,
|
||||
MoveAndSetMaddrThenSendHigh = 7
|
||||
}
|
||||
|
||||
private enum AluOperation
|
||||
{
|
||||
AluReg = 0,
|
||||
AddImmediate = 1,
|
||||
BitfieldReplace = 2,
|
||||
BitfieldExtractLslImm = 3,
|
||||
BitfieldExtractLslReg = 4,
|
||||
ReadImmediate = 5
|
||||
}
|
||||
|
||||
private enum AluRegOperation
|
||||
{
|
||||
Add = 0,
|
||||
AddWithCarry = 1,
|
||||
Subtract = 2,
|
||||
SubtractWithBorrow = 3,
|
||||
BitwiseExclusiveOr = 8,
|
||||
BitwiseOr = 9,
|
||||
BitwiseAnd = 10,
|
||||
BitwiseAndNot = 11,
|
||||
BitwiseNotAnd = 12
|
||||
}
|
||||
|
||||
private NvGpuFifo PFifo;
|
||||
private INvGpuEngine Engine;
|
||||
|
||||
public Queue<int> Fifo { get; private set; }
|
||||
|
||||
private int[] Gprs;
|
||||
|
||||
private int MethAddr;
|
||||
private int MethIncr;
|
||||
|
||||
private bool Carry;
|
||||
|
||||
private int OpCode;
|
||||
|
||||
private int PipeOp;
|
||||
|
||||
private int Pc;
|
||||
|
||||
public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine)
|
||||
{
|
||||
this.PFifo = PFifo;
|
||||
this.Engine = Engine;
|
||||
|
||||
Fifo = new Queue<int>();
|
||||
|
||||
Gprs = new int[8];
|
||||
}
|
||||
|
||||
public void Execute(NvGpuVmm Vmm, int[] Mme, int Position, int Param)
|
||||
{
|
||||
Reset();
|
||||
|
||||
Gprs[1] = Param;
|
||||
|
||||
Pc = Position;
|
||||
|
||||
FetchOpCode(Mme);
|
||||
|
||||
while (Step(Vmm, Mme));
|
||||
|
||||
//Due to the delay slot, we still need to execute
|
||||
//one more instruction before we actually exit.
|
||||
Step(Vmm, Mme);
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
for (int Index = 0; Index < Gprs.Length; Index++)
|
||||
{
|
||||
Gprs[Index] = 0;
|
||||
}
|
||||
|
||||
MethAddr = 0;
|
||||
MethIncr = 0;
|
||||
|
||||
Carry = false;
|
||||
}
|
||||
|
||||
private bool Step(NvGpuVmm Vmm, int[] Mme)
|
||||
{
|
||||
int BaseAddr = Pc - 1;
|
||||
|
||||
FetchOpCode(Mme);
|
||||
|
||||
if ((OpCode & 7) < 7)
|
||||
{
|
||||
//Operation produces a value.
|
||||
AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7);
|
||||
|
||||
int Result = GetAluResult();
|
||||
|
||||
switch (AsgOp)
|
||||
{
|
||||
//Fetch parameter and ignore result.
|
||||
case AssignmentOperation.IgnoreAndFetch:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Move result.
|
||||
case AssignmentOperation.Move:
|
||||
{
|
||||
SetDstGpr(Result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Move result and use as Method Address.
|
||||
case AssignmentOperation.MoveAndSetMaddr:
|
||||
{
|
||||
SetDstGpr(Result);
|
||||
|
||||
SetMethAddr(Result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Fetch parameter and send result.
|
||||
case AssignmentOperation.FetchAndSend:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
Send(Vmm, Result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Move and send result.
|
||||
case AssignmentOperation.MoveAndSend:
|
||||
{
|
||||
SetDstGpr(Result);
|
||||
|
||||
Send(Vmm, Result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Fetch parameter and use result as Method Address.
|
||||
case AssignmentOperation.FetchAndSetMaddr:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
SetMethAddr(Result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Move result and use as Method Address, then fetch and send paramter.
|
||||
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
|
||||
{
|
||||
SetDstGpr(Result);
|
||||
|
||||
SetMethAddr(Result);
|
||||
|
||||
Send(Vmm, FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Move result and use as Method Address, then send bits 17:12 of result.
|
||||
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
|
||||
{
|
||||
SetDstGpr(Result);
|
||||
|
||||
SetMethAddr(Result);
|
||||
|
||||
Send(Vmm, (Result >> 12) & 0x3f);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Branch.
|
||||
bool OnNotZero = ((OpCode >> 4) & 1) != 0;
|
||||
|
||||
bool Taken = OnNotZero
|
||||
? GetGprA() != 0
|
||||
: GetGprA() == 0;
|
||||
|
||||
if (Taken)
|
||||
{
|
||||
Pc = BaseAddr + GetImm();
|
||||
|
||||
bool NoDelays = (OpCode & 0x20) != 0;
|
||||
|
||||
if (NoDelays)
|
||||
{
|
||||
FetchOpCode(Mme);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Exit = (OpCode & 0x80) != 0;
|
||||
|
||||
return !Exit;
|
||||
}
|
||||
|
||||
private void FetchOpCode(int[] Mme)
|
||||
{
|
||||
OpCode = PipeOp;
|
||||
|
||||
PipeOp = Mme[Pc++];
|
||||
}
|
||||
|
||||
private int GetAluResult()
|
||||
{
|
||||
AluOperation Op = (AluOperation)(OpCode & 7);
|
||||
|
||||
switch (Op)
|
||||
{
|
||||
case AluOperation.AluReg:
|
||||
{
|
||||
AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f);
|
||||
|
||||
return GetAluResult(AluOp, GetGprA(), GetGprB());
|
||||
}
|
||||
|
||||
case AluOperation.AddImmediate:
|
||||
{
|
||||
return GetGprA() + GetImm();
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldReplace:
|
||||
case AluOperation.BitfieldExtractLslImm:
|
||||
case AluOperation.BitfieldExtractLslReg:
|
||||
{
|
||||
int BfSrcBit = (OpCode >> 17) & 0x1f;
|
||||
int BfSize = (OpCode >> 22) & 0x1f;
|
||||
int BfDstBit = (OpCode >> 27) & 0x1f;
|
||||
|
||||
int BfMask = (1 << BfSize) - 1;
|
||||
|
||||
int Dst = GetGprA();
|
||||
int Src = GetGprB();
|
||||
|
||||
switch (Op)
|
||||
{
|
||||
case AluOperation.BitfieldReplace:
|
||||
{
|
||||
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
|
||||
|
||||
Dst &= ~(BfMask << BfDstBit);
|
||||
|
||||
Dst |= Src << BfDstBit;
|
||||
|
||||
return Dst;
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldExtractLslImm:
|
||||
{
|
||||
Src = (int)((uint)Src >> Dst) & BfMask;
|
||||
|
||||
return Src << BfDstBit;
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldExtractLslReg:
|
||||
{
|
||||
Src = (int)((uint)Src >> BfSrcBit) & BfMask;
|
||||
|
||||
return Src << Dst;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AluOperation.ReadImmediate:
|
||||
{
|
||||
return Read(GetGprA() + GetImm());
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(OpCode));
|
||||
}
|
||||
|
||||
private int GetAluResult(AluRegOperation AluOp, int A, int B)
|
||||
{
|
||||
switch (AluOp)
|
||||
{
|
||||
case AluRegOperation.Add:
|
||||
{
|
||||
ulong Result = (ulong)A + (ulong)B;
|
||||
|
||||
Carry = Result > 0xffffffff;
|
||||
|
||||
return (int)Result;
|
||||
}
|
||||
|
||||
case AluRegOperation.AddWithCarry:
|
||||
{
|
||||
ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL);
|
||||
|
||||
Carry = Result > 0xffffffff;
|
||||
|
||||
return (int)Result;
|
||||
}
|
||||
|
||||
case AluRegOperation.Subtract:
|
||||
{
|
||||
ulong Result = (ulong)A - (ulong)B;
|
||||
|
||||
Carry = Result < 0x100000000;
|
||||
|
||||
return (int)Result;
|
||||
}
|
||||
|
||||
case AluRegOperation.SubtractWithBorrow:
|
||||
{
|
||||
ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL);
|
||||
|
||||
Carry = Result < 0x100000000;
|
||||
|
||||
return (int)Result;
|
||||
}
|
||||
|
||||
case AluRegOperation.BitwiseExclusiveOr: return A ^ B;
|
||||
case AluRegOperation.BitwiseOr: return A | B;
|
||||
case AluRegOperation.BitwiseAnd: return A & B;
|
||||
case AluRegOperation.BitwiseAndNot: return A & ~B;
|
||||
case AluRegOperation.BitwiseNotAnd: return ~(A & B);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(AluOp));
|
||||
}
|
||||
|
||||
private int GetImm()
|
||||
{
|
||||
//Note: The immediate is signed, the sign-extension is intended here.
|
||||
return OpCode >> 14;
|
||||
}
|
||||
|
||||
private void SetMethAddr(int Value)
|
||||
{
|
||||
MethAddr = (Value >> 0) & 0xfff;
|
||||
MethIncr = (Value >> 12) & 0x3f;
|
||||
}
|
||||
|
||||
private void SetDstGpr(int Value)
|
||||
{
|
||||
Gprs[(OpCode >> 8) & 7] = Value;
|
||||
}
|
||||
|
||||
private int GetGprA()
|
||||
{
|
||||
return GetGprValue((OpCode >> 11) & 7);
|
||||
}
|
||||
|
||||
private int GetGprB()
|
||||
{
|
||||
return GetGprValue((OpCode >> 14) & 7);
|
||||
}
|
||||
|
||||
private int GetGprValue(int Index)
|
||||
{
|
||||
return Index != 0 ? Gprs[Index] : 0;
|
||||
}
|
||||
|
||||
private int FetchParam()
|
||||
{
|
||||
int Value;
|
||||
|
||||
if (!Fifo.TryDequeue(out Value))
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
private int Read(int Reg)
|
||||
{
|
||||
return Engine.Registers[Reg];
|
||||
}
|
||||
|
||||
private void Send(NvGpuVmm Vmm, int Value)
|
||||
{
|
||||
GpuMethodCall MethCall = new GpuMethodCall(MethAddr, Value);
|
||||
|
||||
Engine.CallMethod(Vmm, MethCall);
|
||||
|
||||
MethAddr += MethIncr;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics/Graphics3d/NvGpuEngine.cs
Normal file
11
Ryujinx.Graphics/Graphics3d/NvGpuEngine.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuEngine
|
||||
{
|
||||
_2d = 0x902d,
|
||||
_3d = 0xb197,
|
||||
Compute = 0xb1c0,
|
||||
P2mf = 0xa140,
|
||||
M2mf = 0xb0b5
|
||||
}
|
||||
}
|
175
Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs
Normal file
175
Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs
Normal file
@ -0,0 +1,175 @@
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
class NvGpuEngine2d : INvGpuEngine
|
||||
{
|
||||
private enum CopyOperation
|
||||
{
|
||||
SrcCopyAnd,
|
||||
RopAnd,
|
||||
Blend,
|
||||
SrcCopy,
|
||||
Rop,
|
||||
SrcCopyPremult,
|
||||
BlendPremult
|
||||
}
|
||||
|
||||
public int[] Registers { get; private set; }
|
||||
|
||||
private NvGpu Gpu;
|
||||
|
||||
public NvGpuEngine2d(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
||||
Registers = new int[0x238];
|
||||
}
|
||||
|
||||
public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
WriteRegister(MethCall);
|
||||
|
||||
if ((NvGpuEngine2dReg)MethCall.Method == NvGpuEngine2dReg.BlitSrcYInt)
|
||||
{
|
||||
TextureCopy(Vmm);
|
||||
}
|
||||
}
|
||||
|
||||
private void TextureCopy(NvGpuVmm Vmm)
|
||||
{
|
||||
CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
|
||||
|
||||
int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat);
|
||||
bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
|
||||
int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth);
|
||||
int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
|
||||
int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch);
|
||||
int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
|
||||
|
||||
int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat);
|
||||
bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
|
||||
int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth);
|
||||
int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight);
|
||||
int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch);
|
||||
int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions);
|
||||
|
||||
int DstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX);
|
||||
int DstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY);
|
||||
int DstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW);
|
||||
int DstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH);
|
||||
|
||||
long BlitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract);
|
||||
long BlitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract);
|
||||
|
||||
long SrcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract);
|
||||
long SrcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract);
|
||||
|
||||
GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat);
|
||||
GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat);
|
||||
|
||||
GalMemoryLayout SrcLayout = GetLayout(SrcLinear);
|
||||
GalMemoryLayout DstLayout = GetLayout(DstLinear);
|
||||
|
||||
int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf);
|
||||
int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
|
||||
|
||||
long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress);
|
||||
long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress);
|
||||
|
||||
long SrcKey = Vmm.GetPhysicalAddress(SrcAddress);
|
||||
long DstKey = Vmm.GetPhysicalAddress(DstAddress);
|
||||
|
||||
GalImage SrcTexture = new GalImage(
|
||||
SrcWidth,
|
||||
SrcHeight, 1,
|
||||
SrcBlockHeight,
|
||||
SrcLayout,
|
||||
SrcImgFormat);
|
||||
|
||||
GalImage DstTexture = new GalImage(
|
||||
DstWidth,
|
||||
DstHeight, 1,
|
||||
DstBlockHeight,
|
||||
DstLayout,
|
||||
DstImgFormat);
|
||||
|
||||
SrcTexture.Pitch = SrcPitch;
|
||||
DstTexture.Pitch = DstPitch;
|
||||
|
||||
Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture);
|
||||
Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture);
|
||||
|
||||
int SrcBlitX1 = (int)(SrcBlitX >> 32);
|
||||
int SrcBlitY1 = (int)(SrcBlitY >> 32);
|
||||
|
||||
int SrcBlitX2 = (int)(SrcBlitX + DstBlitW * BlitDuDx >> 32);
|
||||
int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32);
|
||||
|
||||
Gpu.Renderer.RenderTarget.Copy(
|
||||
SrcKey,
|
||||
DstKey,
|
||||
SrcBlitX1,
|
||||
SrcBlitY1,
|
||||
SrcBlitX2,
|
||||
SrcBlitY2,
|
||||
DstBlitX,
|
||||
DstBlitY,
|
||||
DstBlitX + DstBlitW,
|
||||
DstBlitY + DstBlitH);
|
||||
|
||||
//Do a guest side copy aswell. This is necessary when
|
||||
//the texture is modified by the guest, however it doesn't
|
||||
//work when resources that the gpu can write to are copied,
|
||||
//like framebuffers.
|
||||
ImageUtils.CopyTexture(
|
||||
Vmm,
|
||||
SrcTexture,
|
||||
DstTexture,
|
||||
SrcAddress,
|
||||
DstAddress,
|
||||
SrcBlitX1,
|
||||
SrcBlitY1,
|
||||
DstBlitX,
|
||||
DstBlitY,
|
||||
DstBlitW,
|
||||
DstBlitH);
|
||||
|
||||
Vmm.IsRegionModified(DstKey, ImageUtils.GetSize(DstTexture), NvGpuBufferType.Texture);
|
||||
}
|
||||
|
||||
private static GalMemoryLayout GetLayout(bool Linear)
|
||||
{
|
||||
return Linear
|
||||
? GalMemoryLayout.Pitch
|
||||
: GalMemoryLayout.BlockLinear;
|
||||
}
|
||||
|
||||
private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg)
|
||||
{
|
||||
return
|
||||
(long)Registers[(int)Reg + 0] << 32 |
|
||||
(uint)Registers[(int)Reg + 1];
|
||||
}
|
||||
|
||||
private void WriteRegister(GpuMethodCall MethCall)
|
||||
{
|
||||
Registers[MethCall.Method] = MethCall.Argument;
|
||||
}
|
||||
|
||||
private long ReadRegisterFixed1_31_32(NvGpuEngine2dReg Reg)
|
||||
{
|
||||
long Low = (uint)ReadRegister(Reg + 0);
|
||||
long High = (uint)ReadRegister(Reg + 1);
|
||||
|
||||
return Low | (High << 32);
|
||||
}
|
||||
|
||||
private int ReadRegister(NvGpuEngine2dReg Reg)
|
||||
{
|
||||
return Registers[(int)Reg];
|
||||
}
|
||||
}
|
||||
}
|
39
Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs
Normal file
39
Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs
Normal file
@ -0,0 +1,39 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuEngine2dReg
|
||||
{
|
||||
DstFormat = 0x80,
|
||||
DstLinear = 0x81,
|
||||
DstBlockDimensions = 0x82,
|
||||
DstDepth = 0x83,
|
||||
DstLayer = 0x84,
|
||||
DstPitch = 0x85,
|
||||
DstWidth = 0x86,
|
||||
DstHeight = 0x87,
|
||||
DstAddress = 0x88,
|
||||
SrcFormat = 0x8c,
|
||||
SrcLinear = 0x8d,
|
||||
SrcBlockDimensions = 0x8e,
|
||||
SrcDepth = 0x8f,
|
||||
SrcLayer = 0x90,
|
||||
SrcPitch = 0x91,
|
||||
SrcWidth = 0x92,
|
||||
SrcHeight = 0x93,
|
||||
SrcAddress = 0x94,
|
||||
ClipEnable = 0xa4,
|
||||
CopyOperation = 0xab,
|
||||
BlitControl = 0x223,
|
||||
BlitDstX = 0x22c,
|
||||
BlitDstY = 0x22d,
|
||||
BlitDstW = 0x22e,
|
||||
BlitDstH = 0x22f,
|
||||
BlitDuDxFract = 0x230,
|
||||
BlitDuDxInt = 0x231,
|
||||
BlitDvDyFract = 0x232,
|
||||
BlitDvDyInt = 0x233,
|
||||
BlitSrcXFract = 0x234,
|
||||
BlitSrcXInt = 0x235,
|
||||
BlitSrcYFract = 0x236,
|
||||
BlitSrcYInt = 0x237
|
||||
}
|
||||
}
|
1014
Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
Normal file
1014
Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
Normal file
File diff suppressed because it is too large
Load Diff
110
Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
Normal file
110
Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
Normal file
@ -0,0 +1,110 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuEngine3dReg
|
||||
{
|
||||
FrameBufferNAddress = 0x200,
|
||||
FrameBufferNWidth = 0x202,
|
||||
FrameBufferNHeight = 0x203,
|
||||
FrameBufferNFormat = 0x204,
|
||||
FrameBufferNBlockDim = 0x205,
|
||||
ViewportNScaleX = 0x280,
|
||||
ViewportNScaleY = 0x281,
|
||||
ViewportNScaleZ = 0x282,
|
||||
ViewportNTranslateX = 0x283,
|
||||
ViewportNTranslateY = 0x284,
|
||||
ViewportNTranslateZ = 0x285,
|
||||
ViewportNHoriz = 0x300,
|
||||
ViewportNVert = 0x301,
|
||||
DepthRangeNNear = 0x302,
|
||||
DepthRangeNFar = 0x303,
|
||||
VertexArrayFirst = 0x35d,
|
||||
VertexArrayCount = 0x35e,
|
||||
ClearNColor = 0x360,
|
||||
ClearDepth = 0x364,
|
||||
ClearStencil = 0x368,
|
||||
StencilBackFuncRef = 0x3d5,
|
||||
StencilBackMask = 0x3d6,
|
||||
StencilBackFuncMask = 0x3d7,
|
||||
ColorMaskCommon = 0x3e4,
|
||||
RTSeparateFragData = 0x3eb,
|
||||
ZetaAddress = 0x3f8,
|
||||
ZetaFormat = 0x3fa,
|
||||
ZetaBlockDimensions = 0x3fb,
|
||||
ZetaLayerStride = 0x3fc,
|
||||
VertexAttribNFormat = 0x458,
|
||||
RTControl = 0x487,
|
||||
ZetaHoriz = 0x48a,
|
||||
ZetaVert = 0x48b,
|
||||
ZetaArrayMode = 0x48c,
|
||||
LinkedTsc = 0x48d,
|
||||
DepthTestEnable = 0x4b3,
|
||||
BlendIndependent = 0x4b9,
|
||||
DepthWriteEnable = 0x4ba,
|
||||
DepthTestFunction = 0x4c3,
|
||||
BlendSeparateAlpha = 0x4cf,
|
||||
BlendEquationRgb = 0x4d0,
|
||||
BlendFuncSrcRgb = 0x4d1,
|
||||
BlendFuncDstRgb = 0x4d2,
|
||||
BlendEquationAlpha = 0x4d3,
|
||||
BlendFuncSrcAlpha = 0x4d4,
|
||||
BlendFuncDstAlpha = 0x4d6,
|
||||
BlendEnable = 0x4d7,
|
||||
IBlendNEnable = 0x4d8,
|
||||
StencilEnable = 0x4e0,
|
||||
StencilFrontOpFail = 0x4e1,
|
||||
StencilFrontOpZFail = 0x4e2,
|
||||
StencilFrontOpZPass = 0x4e3,
|
||||
StencilFrontFuncFunc = 0x4e4,
|
||||
StencilFrontFuncRef = 0x4e5,
|
||||
StencilFrontFuncMask = 0x4e6,
|
||||
StencilFrontMask = 0x4e7,
|
||||
ScreenYControl = 0x4eb,
|
||||
VertexArrayElemBase = 0x50d,
|
||||
VertexArrayInstBase = 0x50e,
|
||||
ZetaEnable = 0x54e,
|
||||
TexHeaderPoolOffset = 0x55d,
|
||||
TexSamplerPoolOffset = 0x557,
|
||||
StencilTwoSideEnable = 0x565,
|
||||
StencilBackOpFail = 0x566,
|
||||
StencilBackOpZFail = 0x567,
|
||||
StencilBackOpZPass = 0x568,
|
||||
StencilBackFuncFunc = 0x569,
|
||||
FrameBufferSrgb = 0x56e,
|
||||
ShaderAddress = 0x582,
|
||||
VertexBeginGl = 0x586,
|
||||
PrimRestartEnable = 0x591,
|
||||
PrimRestartIndex = 0x592,
|
||||
IndexArrayAddress = 0x5f2,
|
||||
IndexArrayEndAddr = 0x5f4,
|
||||
IndexArrayFormat = 0x5f6,
|
||||
IndexBatchFirst = 0x5f7,
|
||||
IndexBatchCount = 0x5f8,
|
||||
VertexArrayNInstance = 0x620,
|
||||
CullFaceEnable = 0x646,
|
||||
FrontFace = 0x647,
|
||||
CullFace = 0x648,
|
||||
ColorMaskN = 0x680,
|
||||
QueryAddress = 0x6c0,
|
||||
QuerySequence = 0x6c2,
|
||||
QueryControl = 0x6c3,
|
||||
VertexArrayNControl = 0x700,
|
||||
VertexArrayNAddress = 0x701,
|
||||
VertexArrayNDivisor = 0x703,
|
||||
IBlendNSeparateAlpha = 0x780,
|
||||
IBlendNEquationRgb = 0x781,
|
||||
IBlendNFuncSrcRgb = 0x782,
|
||||
IBlendNFuncDstRgb = 0x783,
|
||||
IBlendNEquationAlpha = 0x784,
|
||||
IBlendNFuncSrcAlpha = 0x785,
|
||||
IBlendNFuncDstAlpha = 0x786,
|
||||
VertexArrayNEndAddr = 0x7c0,
|
||||
ShaderNControl = 0x800,
|
||||
ShaderNOffset = 0x801,
|
||||
ShaderNMaxGprs = 0x803,
|
||||
ShaderNType = 0x804,
|
||||
ConstBufferSize = 0x8e0,
|
||||
ConstBufferAddress = 0x8e1,
|
||||
ConstBufferOffset = 0x8e3,
|
||||
TextureCbIndex = 0x982
|
||||
}
|
||||
}
|
187
Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs
Normal file
187
Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs
Normal file
@ -0,0 +1,187 @@
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
class NvGpuEngineM2mf : INvGpuEngine
|
||||
{
|
||||
public int[] Registers { get; private set; }
|
||||
|
||||
private NvGpu Gpu;
|
||||
|
||||
private Dictionary<int, NvGpuMethod> Methods;
|
||||
|
||||
public NvGpuEngineM2mf(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
||||
Registers = new int[0x1d6];
|
||||
|
||||
Methods = new Dictionary<int, NvGpuMethod>();
|
||||
|
||||
void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
|
||||
{
|
||||
while (Count-- > 0)
|
||||
{
|
||||
Methods.Add(Meth, Method);
|
||||
|
||||
Meth += Stride;
|
||||
}
|
||||
}
|
||||
|
||||
AddMethod(0xc0, 1, 1, Execute);
|
||||
}
|
||||
|
||||
public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method))
|
||||
{
|
||||
Method(Vmm, MethCall);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteRegister(MethCall);
|
||||
}
|
||||
}
|
||||
|
||||
private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
//TODO: Some registers and copy modes are still not implemented.
|
||||
int Control = MethCall.Argument;
|
||||
|
||||
bool SrcLinear = ((Control >> 7) & 1) != 0;
|
||||
bool DstLinear = ((Control >> 8) & 1) != 0;
|
||||
bool Copy2d = ((Control >> 9) & 1) != 0;
|
||||
|
||||
long SrcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress);
|
||||
long DstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress);
|
||||
|
||||
int SrcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch);
|
||||
int DstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch);
|
||||
|
||||
int XCount = ReadRegister(NvGpuEngineM2mfReg.XCount);
|
||||
int YCount = ReadRegister(NvGpuEngineM2mfReg.YCount);
|
||||
|
||||
int Swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle);
|
||||
|
||||
int DstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim);
|
||||
int DstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX);
|
||||
int DstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY);
|
||||
int DstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ);
|
||||
int DstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY);
|
||||
int DstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ);
|
||||
|
||||
int SrcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim);
|
||||
int SrcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX);
|
||||
int SrcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY);
|
||||
int SrcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ);
|
||||
int SrcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY);
|
||||
int SrcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ);
|
||||
|
||||
int SrcCpp = ((Swizzle >> 20) & 7) + 1;
|
||||
int DstCpp = ((Swizzle >> 24) & 7) + 1;
|
||||
|
||||
int DstPosX = (DstPosXY >> 0) & 0xffff;
|
||||
int DstPosY = (DstPosXY >> 16) & 0xffff;
|
||||
|
||||
int SrcPosX = (SrcPosXY >> 0) & 0xffff;
|
||||
int SrcPosY = (SrcPosXY >> 16) & 0xffff;
|
||||
|
||||
int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf);
|
||||
int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
|
||||
|
||||
long SrcPA = Vmm.GetPhysicalAddress(SrcAddress);
|
||||
long DstPA = Vmm.GetPhysicalAddress(DstAddress);
|
||||
|
||||
if (Copy2d)
|
||||
{
|
||||
if (SrcLinear)
|
||||
{
|
||||
SrcPosX = SrcPosY = SrcPosZ = 0;
|
||||
}
|
||||
|
||||
if (DstLinear)
|
||||
{
|
||||
DstPosX = DstPosY = DstPosZ = 0;
|
||||
}
|
||||
|
||||
if (SrcLinear && DstLinear)
|
||||
{
|
||||
for (int Y = 0; Y < YCount; Y++)
|
||||
{
|
||||
int SrcOffset = (SrcPosY + Y) * SrcPitch + SrcPosX * SrcCpp;
|
||||
int DstOffset = (DstPosY + Y) * DstPitch + DstPosX * DstCpp;
|
||||
|
||||
long Src = SrcPA + (uint)SrcOffset;
|
||||
long Dst = DstPA + (uint)DstOffset;
|
||||
|
||||
Vmm.Memory.CopyBytes(Src, Dst, XCount * SrcCpp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ISwizzle SrcSwizzle;
|
||||
|
||||
if (SrcLinear)
|
||||
{
|
||||
SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight);
|
||||
}
|
||||
|
||||
ISwizzle DstSwizzle;
|
||||
|
||||
if (DstLinear)
|
||||
{
|
||||
DstSwizzle = new LinearSwizzle(DstPitch, DstCpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight);
|
||||
}
|
||||
|
||||
for (int Y = 0; Y < YCount; Y++)
|
||||
for (int X = 0; X < XCount; X++)
|
||||
{
|
||||
int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y);
|
||||
int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y);
|
||||
|
||||
long Src = SrcPA + (uint)SrcOffset;
|
||||
long Dst = DstPA + (uint)DstOffset;
|
||||
|
||||
Vmm.Memory.CopyBytes(Src, Dst, SrcCpp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vmm.Memory.CopyBytes(SrcPA, DstPA, XCount);
|
||||
}
|
||||
}
|
||||
|
||||
private long MakeInt64From2xInt32(NvGpuEngineM2mfReg Reg)
|
||||
{
|
||||
return
|
||||
(long)Registers[(int)Reg + 0] << 32 |
|
||||
(uint)Registers[(int)Reg + 1];
|
||||
}
|
||||
|
||||
private void WriteRegister(GpuMethodCall MethCall)
|
||||
{
|
||||
Registers[MethCall.Method] = MethCall.Argument;
|
||||
}
|
||||
|
||||
private int ReadRegister(NvGpuEngineM2mfReg Reg)
|
||||
{
|
||||
return Registers[(int)Reg];
|
||||
}
|
||||
|
||||
private void WriteRegister(NvGpuEngineM2mfReg Reg, int Value)
|
||||
{
|
||||
Registers[(int)Reg] = Value;
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mfReg.cs
Normal file
25
Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mfReg.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuEngineM2mfReg
|
||||
{
|
||||
SrcAddress = 0x100,
|
||||
DstAddress = 0x102,
|
||||
SrcPitch = 0x104,
|
||||
DstPitch = 0x105,
|
||||
XCount = 0x106,
|
||||
YCount = 0x107,
|
||||
Swizzle = 0x1c2,
|
||||
DstBlkDim = 0x1c3,
|
||||
DstSizeX = 0x1c4,
|
||||
DstSizeY = 0x1c5,
|
||||
DstSizeZ = 0x1c6,
|
||||
DstPosZ = 0x1c7,
|
||||
DstPosXY = 0x1c8,
|
||||
SrcBlkDim = 0x1ca,
|
||||
SrcSizeX = 0x1cb,
|
||||
SrcSizeY = 0x1cc,
|
||||
SrcSizeZ = 0x1cd,
|
||||
SrcPosZ = 0x1ce,
|
||||
SrcPosXY = 0x1cf
|
||||
}
|
||||
}
|
161
Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs
Normal file
161
Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
class NvGpuEngineP2mf : INvGpuEngine
|
||||
{
|
||||
public int[] Registers { get; private set; }
|
||||
|
||||
private NvGpu Gpu;
|
||||
|
||||
private Dictionary<int, NvGpuMethod> Methods;
|
||||
|
||||
private int CopyStartX;
|
||||
private int CopyStartY;
|
||||
|
||||
private int CopyWidth;
|
||||
private int CopyHeight;
|
||||
private int CopyGobBlockHeight;
|
||||
|
||||
private long CopyAddress;
|
||||
|
||||
private int CopyOffset;
|
||||
private int CopySize;
|
||||
|
||||
private bool CopyLinear;
|
||||
|
||||
private byte[] Buffer;
|
||||
|
||||
public NvGpuEngineP2mf(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
||||
Registers = new int[0x80];
|
||||
|
||||
Methods = new Dictionary<int, NvGpuMethod>();
|
||||
|
||||
void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
|
||||
{
|
||||
while (Count-- > 0)
|
||||
{
|
||||
Methods.Add(Meth, Method);
|
||||
|
||||
Meth += Stride;
|
||||
}
|
||||
}
|
||||
|
||||
AddMethod(0x6c, 1, 1, Execute);
|
||||
AddMethod(0x6d, 1, 1, PushData);
|
||||
}
|
||||
|
||||
public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method))
|
||||
{
|
||||
Method(Vmm, MethCall);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteRegister(MethCall);
|
||||
}
|
||||
}
|
||||
|
||||
private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
//TODO: Some registers and copy modes are still not implemented.
|
||||
int Control = MethCall.Argument;
|
||||
|
||||
long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress);
|
||||
|
||||
int DstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch);
|
||||
int DstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim);
|
||||
|
||||
int DstX = ReadRegister(NvGpuEngineP2mfReg.DstX);
|
||||
int DstY = ReadRegister(NvGpuEngineP2mfReg.DstY);
|
||||
|
||||
int DstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth);
|
||||
int DstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight);
|
||||
|
||||
int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn);
|
||||
int LineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount);
|
||||
|
||||
CopyLinear = (Control & 1) != 0;
|
||||
|
||||
CopyGobBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
|
||||
|
||||
CopyStartX = DstX;
|
||||
CopyStartY = DstY;
|
||||
|
||||
CopyWidth = DstWidth;
|
||||
CopyHeight = DstHeight;
|
||||
|
||||
CopyAddress = DstAddress;
|
||||
|
||||
CopyOffset = 0;
|
||||
CopySize = LineLengthIn * LineCount;
|
||||
|
||||
Buffer = new byte[CopySize];
|
||||
}
|
||||
|
||||
private void PushData(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
if (Buffer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int Shift = 0; Shift < 32 && CopyOffset < CopySize; Shift += 8, CopyOffset++)
|
||||
{
|
||||
Buffer[CopyOffset] = (byte)(MethCall.Argument >> Shift);
|
||||
}
|
||||
|
||||
if (MethCall.IsLastCall)
|
||||
{
|
||||
if (CopyLinear)
|
||||
{
|
||||
Vmm.WriteBytes(CopyAddress, Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight);
|
||||
|
||||
int SrcOffset = 0;
|
||||
|
||||
for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++)
|
||||
for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++)
|
||||
{
|
||||
int DstOffset = Swizzle.GetSwizzleOffset(X, Y);
|
||||
|
||||
Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]);
|
||||
}
|
||||
}
|
||||
|
||||
Buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg)
|
||||
{
|
||||
return
|
||||
(long)Registers[(int)Reg + 0] << 32 |
|
||||
(uint)Registers[(int)Reg + 1];
|
||||
}
|
||||
|
||||
private void WriteRegister(GpuMethodCall MethCall)
|
||||
{
|
||||
Registers[MethCall.Method] = MethCall.Argument;
|
||||
}
|
||||
|
||||
private int ReadRegister(NvGpuEngineP2mfReg Reg)
|
||||
{
|
||||
return Registers[(int)Reg];
|
||||
}
|
||||
|
||||
private void WriteRegister(NvGpuEngineP2mfReg Reg, int Value)
|
||||
{
|
||||
Registers[(int)Reg] = Value;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mfReg.cs
Normal file
17
Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mfReg.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuEngineP2mfReg
|
||||
{
|
||||
LineLengthIn = 0x60,
|
||||
LineCount = 0x61,
|
||||
DstAddress = 0x62,
|
||||
DstPitch = 0x64,
|
||||
DstBlockDim = 0x65,
|
||||
DstWidth = 0x66,
|
||||
DstHeight = 0x67,
|
||||
DstDepth = 0x68,
|
||||
DstZ = 0x69,
|
||||
DstX = 0x6a,
|
||||
DstY = 0x6b
|
||||
}
|
||||
}
|
176
Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs
Normal file
176
Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using Ryujinx.Graphics.Memory;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
class NvGpuFifo
|
||||
{
|
||||
private const int MacrosCount = 0x80;
|
||||
private const int MacroIndexMask = MacrosCount - 1;
|
||||
|
||||
//Note: The size of the macro memory is unknown, we just make
|
||||
//a guess here and use 256kb as the size. Increase if needed.
|
||||
private const int MmeWords = 256 * 256;
|
||||
|
||||
private NvGpu Gpu;
|
||||
|
||||
private NvGpuEngine[] SubChannels;
|
||||
|
||||
private struct CachedMacro
|
||||
{
|
||||
public int Position { get; private set; }
|
||||
|
||||
private bool ExecutionPending;
|
||||
private int Argument;
|
||||
|
||||
private MacroInterpreter Interpreter;
|
||||
|
||||
public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position)
|
||||
{
|
||||
this.Position = Position;
|
||||
|
||||
ExecutionPending = false;
|
||||
Argument = 0;
|
||||
|
||||
Interpreter = new MacroInterpreter(PFifo, Engine);
|
||||
}
|
||||
|
||||
public void StartExecution(int Argument)
|
||||
{
|
||||
this.Argument = Argument;
|
||||
|
||||
ExecutionPending = true;
|
||||
}
|
||||
|
||||
public void Execute(NvGpuVmm Vmm, int[] Mme)
|
||||
{
|
||||
if (ExecutionPending)
|
||||
{
|
||||
ExecutionPending = false;
|
||||
|
||||
Interpreter?.Execute(Vmm, Mme, Position, Argument);
|
||||
}
|
||||
}
|
||||
|
||||
public void PushArgument(int Argument)
|
||||
{
|
||||
Interpreter?.Fifo.Enqueue(Argument);
|
||||
}
|
||||
}
|
||||
|
||||
private int CurrMacroPosition;
|
||||
private int CurrMacroBindIndex;
|
||||
|
||||
private CachedMacro[] Macros;
|
||||
|
||||
private int[] Mme;
|
||||
|
||||
public NvGpuFifo(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
||||
SubChannels = new NvGpuEngine[8];
|
||||
|
||||
Macros = new CachedMacro[MacrosCount];
|
||||
|
||||
Mme = new int[MmeWords];
|
||||
}
|
||||
|
||||
public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
if ((NvGpuFifoMeth)MethCall.Method == NvGpuFifoMeth.BindChannel)
|
||||
{
|
||||
NvGpuEngine Engine = (NvGpuEngine)MethCall.Argument;
|
||||
|
||||
SubChannels[MethCall.SubChannel] = Engine;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (SubChannels[MethCall.SubChannel])
|
||||
{
|
||||
case NvGpuEngine._2d: Call2dMethod (Vmm, MethCall); break;
|
||||
case NvGpuEngine._3d: Call3dMethod (Vmm, MethCall); break;
|
||||
case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, MethCall); break;
|
||||
case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, MethCall); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Call2dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
Gpu.Engine2d.CallMethod(Vmm, MethCall);
|
||||
}
|
||||
|
||||
private void Call3dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
if (MethCall.Method < 0x80)
|
||||
{
|
||||
switch ((NvGpuFifoMeth)MethCall.Method)
|
||||
{
|
||||
case NvGpuFifoMeth.SetMacroUploadAddress:
|
||||
{
|
||||
CurrMacroPosition = MethCall.Argument;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case NvGpuFifoMeth.SendMacroCodeData:
|
||||
{
|
||||
Mme[CurrMacroPosition++] = MethCall.Argument;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case NvGpuFifoMeth.SetMacroBindingIndex:
|
||||
{
|
||||
CurrMacroBindIndex = MethCall.Argument;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case NvGpuFifoMeth.BindMacro:
|
||||
{
|
||||
int Position = MethCall.Argument;
|
||||
|
||||
Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: CallP2mfMethod(Vmm, MethCall); break;
|
||||
}
|
||||
}
|
||||
else if (MethCall.Method < 0xe00)
|
||||
{
|
||||
Gpu.Engine3d.CallMethod(Vmm, MethCall);
|
||||
}
|
||||
else
|
||||
{
|
||||
int MacroIndex = (MethCall.Method >> 1) & MacroIndexMask;
|
||||
|
||||
if ((MethCall.Method & 1) != 0)
|
||||
{
|
||||
Macros[MacroIndex].PushArgument(MethCall.Argument);
|
||||
}
|
||||
else
|
||||
{
|
||||
Macros[MacroIndex].StartExecution(MethCall.Argument);
|
||||
}
|
||||
|
||||
if (MethCall.IsLastCall)
|
||||
{
|
||||
Macros[MacroIndex].Execute(Vmm, Mme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CallP2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
Gpu.EngineP2mf.CallMethod(Vmm, MethCall);
|
||||
}
|
||||
|
||||
private void CallM2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
|
||||
{
|
||||
Gpu.EngineM2mf.CallMethod(Vmm, MethCall);
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics/Graphics3d/NvGpuFifoMeth.cs
Normal file
11
Ryujinx.Graphics/Graphics3d/NvGpuFifoMeth.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
enum NvGpuFifoMeth
|
||||
{
|
||||
BindChannel = 0,
|
||||
SetMacroUploadAddress = 0x45,
|
||||
SendMacroCodeData = 0x46,
|
||||
SetMacroBindingIndex = 0x47,
|
||||
BindMacro = 0x48
|
||||
}
|
||||
}
|
6
Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs
Normal file
6
Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using Ryujinx.Graphics.Memory;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
delegate void NvGpuMethod(NvGpuVmm Vmm, GpuMethodCall MethCall);
|
||||
}
|
1384
Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs
Normal file
1384
Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs
Normal file
File diff suppressed because it is too large
Load Diff
138
Ryujinx.Graphics/Graphics3d/Texture/ASTCPixel.cs
Normal file
138
Ryujinx.Graphics/Graphics3d/Texture/ASTCPixel.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
class ASTCPixel
|
||||
{
|
||||
public short R { get; set; }
|
||||
public short G { get; set; }
|
||||
public short B { get; set; }
|
||||
public short A { get; set; }
|
||||
|
||||
byte[] BitDepth = new byte[4];
|
||||
|
||||
public ASTCPixel(short _A, short _R, short _G, short _B)
|
||||
{
|
||||
A = _A;
|
||||
R = _R;
|
||||
G = _G;
|
||||
B = _B;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
BitDepth[i] = 8;
|
||||
}
|
||||
|
||||
public void ClampByte()
|
||||
{
|
||||
R = Math.Min(Math.Max(R, (short)0), (short)255);
|
||||
G = Math.Min(Math.Max(G, (short)0), (short)255);
|
||||
B = Math.Min(Math.Max(B, (short)0), (short)255);
|
||||
A = Math.Min(Math.Max(A, (short)0), (short)255);
|
||||
}
|
||||
|
||||
public short GetComponent(int Index)
|
||||
{
|
||||
switch(Index)
|
||||
{
|
||||
case 0: return A;
|
||||
case 1: return R;
|
||||
case 2: return G;
|
||||
case 3: return B;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void SetComponent(int Index, int Value)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
A = (short)Value;
|
||||
break;
|
||||
case 1:
|
||||
R = (short)Value;
|
||||
break;
|
||||
case 2:
|
||||
G = (short)Value;
|
||||
break;
|
||||
case 3:
|
||||
B = (short)Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeBitDepth(byte[] Depth)
|
||||
{
|
||||
for(int i = 0; i< 4; i++)
|
||||
{
|
||||
int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
|
||||
|
||||
SetComponent(i, Value);
|
||||
BitDepth[i] = Depth[i];
|
||||
}
|
||||
}
|
||||
|
||||
short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
|
||||
{
|
||||
Debug.Assert(NewDepth <= 8);
|
||||
Debug.Assert(OldDepth <= 8);
|
||||
|
||||
if (OldDepth == NewDepth)
|
||||
{
|
||||
// Do nothing
|
||||
return Value;
|
||||
}
|
||||
else if (OldDepth == 0 && NewDepth != 0)
|
||||
{
|
||||
return (short)((1 << NewDepth) - 1);
|
||||
}
|
||||
else if (NewDepth > OldDepth)
|
||||
{
|
||||
return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
// oldDepth > newDepth
|
||||
if (NewDepth == 0)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte BitsWasted = (byte)(OldDepth - NewDepth);
|
||||
short TempValue = Value;
|
||||
|
||||
TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
|
||||
TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
|
||||
|
||||
return (byte)(TempValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Pack()
|
||||
{
|
||||
ASTCPixel NewPixel = new ASTCPixel(A, R, G, B);
|
||||
byte[] eightBitDepth = { 8, 8, 8, 8 };
|
||||
|
||||
NewPixel.ChangeBitDepth(eightBitDepth);
|
||||
|
||||
return (byte)NewPixel.A << 24 |
|
||||
(byte)NewPixel.B << 16 |
|
||||
(byte)NewPixel.G << 8 |
|
||||
(byte)NewPixel.R << 0;
|
||||
}
|
||||
|
||||
// Adds more precision to the blue channel as described
|
||||
// in C.2.14
|
||||
public static ASTCPixel BlueContract(int a, int r, int g, int b)
|
||||
{
|
||||
return new ASTCPixel((short)(a),
|
||||
(short)((r + b) >> 1),
|
||||
(short)((g + b) >> 1),
|
||||
(short)(b));
|
||||
}
|
||||
}
|
||||
}
|
121
Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs
Normal file
121
Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public class BitArrayStream
|
||||
{
|
||||
public BitArray BitsArray;
|
||||
|
||||
public int Position { get; private set; }
|
||||
|
||||
public BitArrayStream(BitArray BitArray)
|
||||
{
|
||||
BitsArray = BitArray;
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public short ReadBits(int Length)
|
||||
{
|
||||
int RetValue = 0;
|
||||
for (int i = Position; i < Position + Length; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
RetValue |= 1 << (i - Position);
|
||||
}
|
||||
}
|
||||
|
||||
Position += Length;
|
||||
return (short)RetValue;
|
||||
}
|
||||
|
||||
public int ReadBits(int Start, int End)
|
||||
{
|
||||
int RetValue = 0;
|
||||
for (int i = Start; i <= End; i++)
|
||||
{
|
||||
if (BitsArray[i])
|
||||
{
|
||||
RetValue |= 1 << (i - Start);
|
||||
}
|
||||
}
|
||||
|
||||
return RetValue;
|
||||
}
|
||||
|
||||
public int ReadBit(int Index)
|
||||
{
|
||||
return Convert.ToInt32(BitsArray[Index]);
|
||||
}
|
||||
|
||||
public void WriteBits(int Value, int Length)
|
||||
{
|
||||
for (int i = Position; i < Position + Length; i++)
|
||||
{
|
||||
BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
|
||||
}
|
||||
|
||||
Position += Length;
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
|
||||
BitsArray.CopyTo(RetArray, 0);
|
||||
return RetArray;
|
||||
}
|
||||
|
||||
public static int Replicate(int Value, int NumberBits, int ToBit)
|
||||
{
|
||||
if (NumberBits == 0) return 0;
|
||||
if (ToBit == 0) return 0;
|
||||
|
||||
int TempValue = Value & ((1 << NumberBits) - 1);
|
||||
int RetValue = TempValue;
|
||||
int ResLength = NumberBits;
|
||||
|
||||
while (ResLength < ToBit)
|
||||
{
|
||||
int Comp = 0;
|
||||
if (NumberBits > ToBit - ResLength)
|
||||
{
|
||||
int NewShift = ToBit - ResLength;
|
||||
Comp = NumberBits - NewShift;
|
||||
NumberBits = NewShift;
|
||||
}
|
||||
RetValue <<= NumberBits;
|
||||
RetValue |= TempValue >> Comp;
|
||||
ResLength += NumberBits;
|
||||
}
|
||||
return RetValue;
|
||||
}
|
||||
|
||||
public static int PopCnt(int Number)
|
||||
{
|
||||
int Counter;
|
||||
for (Counter = 0; Number != 0; Counter++)
|
||||
{
|
||||
Number &= Number - 1;
|
||||
}
|
||||
return Counter;
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T Temp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = Temp;
|
||||
}
|
||||
|
||||
// Transfers a bit as described in C.2.14
|
||||
public static void BitTransferSigned(ref int a, ref int b)
|
||||
{
|
||||
b >>= 1;
|
||||
b |= a & 0x80;
|
||||
a >>= 1;
|
||||
a &= 0x3F;
|
||||
if ((a & 0x20) != 0) a -= 0x40;
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
Normal file
59
Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
class BlockLinearSwizzle : ISwizzle
|
||||
{
|
||||
private int BhShift;
|
||||
private int BppShift;
|
||||
private int BhMask;
|
||||
|
||||
private int XShift;
|
||||
private int GobStride;
|
||||
|
||||
public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16)
|
||||
{
|
||||
BhMask = (BlockHeight * 8) - 1;
|
||||
|
||||
BhShift = CountLsbZeros(BlockHeight * 8);
|
||||
BppShift = CountLsbZeros(Bpp);
|
||||
|
||||
int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f);
|
||||
|
||||
GobStride = 512 * BlockHeight * WidthInGobs;
|
||||
|
||||
XShift = CountLsbZeros(512 * BlockHeight);
|
||||
}
|
||||
|
||||
private int CountLsbZeros(int Value)
|
||||
{
|
||||
int Count = 0;
|
||||
|
||||
while (((Value >> Count) & 1) == 0)
|
||||
{
|
||||
Count++;
|
||||
}
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
public int GetSwizzleOffset(int X, int Y)
|
||||
{
|
||||
X <<= BppShift;
|
||||
|
||||
int Position = (Y >> BhShift) * GobStride;
|
||||
|
||||
Position += (X >> 6) << XShift;
|
||||
|
||||
Position += ((Y & BhMask) >> 3) << 9;
|
||||
|
||||
Position += ((X & 0x3f) >> 5) << 8;
|
||||
Position += ((Y & 0x07) >> 1) << 6;
|
||||
Position += ((X & 0x1f) >> 4) << 5;
|
||||
Position += ((Y & 0x01) >> 0) << 4;
|
||||
Position += ((X & 0x0f) >> 0) << 0;
|
||||
|
||||
return Position;
|
||||
}
|
||||
}
|
||||
}
|
7
Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs
Normal file
7
Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
interface ISwizzle
|
||||
{
|
||||
int GetSwizzleOffset(int X, int Y);
|
||||
}
|
||||
}
|
445
Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs
Normal file
445
Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs
Normal file
@ -0,0 +1,445 @@
|
||||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public static class ImageUtils
|
||||
{
|
||||
[Flags]
|
||||
private enum TargetBuffer
|
||||
{
|
||||
Color = 1 << 0,
|
||||
Depth = 1 << 1,
|
||||
Stencil = 1 << 2,
|
||||
|
||||
DepthStencil = Depth | Stencil
|
||||
}
|
||||
|
||||
private struct ImageDescriptor
|
||||
{
|
||||
public int BytesPerPixel { get; private set; }
|
||||
public int BlockWidth { get; private set; }
|
||||
public int BlockHeight { get; private set; }
|
||||
|
||||
public TargetBuffer Target { get; private set; }
|
||||
|
||||
public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target)
|
||||
{
|
||||
this.BytesPerPixel = BytesPerPixel;
|
||||
this.BlockWidth = BlockWidth;
|
||||
this.BlockHeight = BlockHeight;
|
||||
this.Target = Target;
|
||||
}
|
||||
}
|
||||
|
||||
private const GalImageFormat Snorm = GalImageFormat.Snorm;
|
||||
private const GalImageFormat Unorm = GalImageFormat.Unorm;
|
||||
private const GalImageFormat Sint = GalImageFormat.Sint;
|
||||
private const GalImageFormat Uint = GalImageFormat.Uint;
|
||||
private const GalImageFormat Float = GalImageFormat.Float;
|
||||
private const GalImageFormat Srgb = GalImageFormat.Srgb;
|
||||
|
||||
private static readonly Dictionary<GalTextureFormat, GalImageFormat> s_TextureTable =
|
||||
new Dictionary<GalTextureFormat, GalImageFormat>()
|
||||
{
|
||||
{ GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float },
|
||||
{ GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float },
|
||||
{ GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float },
|
||||
{ GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb },
|
||||
{ GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint },
|
||||
{ GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint },
|
||||
{ GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float },
|
||||
{ GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint },
|
||||
{ GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Float },
|
||||
{ GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float },
|
||||
{ GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm },
|
||||
{ GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm },
|
||||
{ GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm },
|
||||
{ GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float },
|
||||
{ GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint },
|
||||
{ GalTextureFormat.D32F, GalImageFormat.D32 | Float },
|
||||
{ GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Float },
|
||||
{ GalTextureFormat.D16, GalImageFormat.D16 | Unorm },
|
||||
|
||||
//Compressed formats
|
||||
{ GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float },
|
||||
{ GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float },
|
||||
{ GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb },
|
||||
{ GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb },
|
||||
{ GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb },
|
||||
{ GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb },
|
||||
{ GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm },
|
||||
{ GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm },
|
||||
{ GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb },
|
||||
{ GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
|
||||
new Dictionary<GalImageFormat, ImageDescriptor>()
|
||||
{
|
||||
{ GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) },
|
||||
{ GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) },
|
||||
|
||||
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
|
||||
{ GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
|
||||
{ GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
|
||||
{ GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
|
||||
{ GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }
|
||||
};
|
||||
|
||||
public static GalImageFormat ConvertTexture(
|
||||
GalTextureFormat Format,
|
||||
GalTextureType RType,
|
||||
GalTextureType GType,
|
||||
GalTextureType BType,
|
||||
GalTextureType AType,
|
||||
bool ConvSrgb)
|
||||
{
|
||||
if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat))
|
||||
{
|
||||
throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!");
|
||||
}
|
||||
|
||||
if (!HasDepth(ImageFormat) && (RType != GType || RType != BType || RType != AType))
|
||||
{
|
||||
throw new NotImplementedException($"Per component types are not implemented!");
|
||||
}
|
||||
|
||||
GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType);
|
||||
|
||||
GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType;
|
||||
|
||||
if (!ImageFormat.HasFlag(FormatType))
|
||||
{
|
||||
throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!");
|
||||
}
|
||||
|
||||
return CombinedFormat;
|
||||
}
|
||||
|
||||
public static GalImageFormat ConvertSurface(GalSurfaceFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float;
|
||||
case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint;
|
||||
case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float;
|
||||
case GalSurfaceFormat.RGBA16Unorm: return GalImageFormat.RGBA16 | Unorm;
|
||||
case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float;
|
||||
case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint;
|
||||
case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint;
|
||||
case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm;
|
||||
case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb;
|
||||
case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm;
|
||||
case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm;
|
||||
case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb;
|
||||
case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm;
|
||||
case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm;
|
||||
case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm;
|
||||
case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float;
|
||||
case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float;
|
||||
case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float;
|
||||
case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint;
|
||||
case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm;
|
||||
case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm;
|
||||
case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float;
|
||||
case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm;
|
||||
case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint;
|
||||
case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm;
|
||||
case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint;
|
||||
case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm;
|
||||
case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm;
|
||||
case GalSurfaceFormat.RGBX8Unorm: return GalImageFormat.RGBX8 | Unorm;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
}
|
||||
|
||||
public static GalImageFormat ConvertZeta(GalZetaFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float;
|
||||
case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm;
|
||||
case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm;
|
||||
case GalZetaFormat.D24X8Unorm: return GalImageFormat.D24 | Unorm;
|
||||
case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm;
|
||||
case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
}
|
||||
|
||||
public static byte[] ReadTexture(IMemory Memory, GalImage Image, long Position)
|
||||
{
|
||||
MemoryManager CpuMemory;
|
||||
|
||||
if (Memory is NvGpuVmm Vmm)
|
||||
{
|
||||
CpuMemory = Vmm.Memory;
|
||||
}
|
||||
else
|
||||
{
|
||||
CpuMemory = (MemoryManager)Memory;
|
||||
}
|
||||
|
||||
ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
|
||||
|
||||
ImageDescriptor Desc = GetImageDescriptor(Image.Format);
|
||||
|
||||
(int Width, int Height) = GetImageSizeInBlocks(Image);
|
||||
|
||||
int BytesPerPixel = Desc.BytesPerPixel;
|
||||
|
||||
//Note: Each row of the texture needs to be aligned to 4 bytes.
|
||||
int Pitch = (Width * BytesPerPixel + 3) & ~3;
|
||||
|
||||
byte[] Data = new byte[Height * Pitch];
|
||||
|
||||
for (int Y = 0; Y < Height; Y++)
|
||||
{
|
||||
int OutOffs = Y * Pitch;
|
||||
|
||||
for (int X = 0; X < Width; X++)
|
||||
{
|
||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||
|
||||
CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel);
|
||||
|
||||
OutOffs += BytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
public static void WriteTexture(NvGpuVmm Vmm, GalImage Image, long Position, byte[] Data)
|
||||
{
|
||||
ISwizzle Swizzle = TextureHelper.GetSwizzle(Image);
|
||||
|
||||
ImageDescriptor Desc = GetImageDescriptor(Image.Format);
|
||||
|
||||
(int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image);
|
||||
|
||||
int BytesPerPixel = Desc.BytesPerPixel;
|
||||
|
||||
int InOffs = 0;
|
||||
|
||||
for (int Y = 0; Y < Height; Y++)
|
||||
for (int X = 0; X < Width; X++)
|
||||
{
|
||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||
|
||||
Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel);
|
||||
|
||||
InOffs += BytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CopyTexture(
|
||||
NvGpuVmm Vmm,
|
||||
GalImage SrcImage,
|
||||
GalImage DstImage,
|
||||
long SrcAddress,
|
||||
long DstAddress,
|
||||
int SrcX,
|
||||
int SrcY,
|
||||
int DstX,
|
||||
int DstY,
|
||||
int Width,
|
||||
int Height)
|
||||
{
|
||||
ISwizzle SrcSwizzle = TextureHelper.GetSwizzle(SrcImage);
|
||||
ISwizzle DstSwizzle = TextureHelper.GetSwizzle(DstImage);
|
||||
|
||||
ImageDescriptor Desc = GetImageDescriptor(SrcImage.Format);
|
||||
|
||||
if (GetImageDescriptor(DstImage.Format).BytesPerPixel != Desc.BytesPerPixel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int BytesPerPixel = Desc.BytesPerPixel;
|
||||
|
||||
for (int Y = 0; Y < Height; Y++)
|
||||
for (int X = 0; X < Width; X++)
|
||||
{
|
||||
long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y);
|
||||
long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y);
|
||||
|
||||
byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel);
|
||||
|
||||
Vmm.WriteBytes(DstAddress + DstOffset, Texel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int GetSize(GalImage Image)
|
||||
{
|
||||
ImageDescriptor Desc = GetImageDescriptor(Image.Format);
|
||||
|
||||
int Width = DivRoundUp(Image.Width, Desc.BlockWidth);
|
||||
int Height = DivRoundUp(Image.Height, Desc.BlockHeight);
|
||||
|
||||
return Desc.BytesPerPixel * Width * Height;
|
||||
}
|
||||
|
||||
public static int GetPitch(GalImageFormat Format, int Width)
|
||||
{
|
||||
ImageDescriptor Desc = GetImageDescriptor(Format);
|
||||
|
||||
int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth);
|
||||
|
||||
Pitch = (Pitch + 0x1f) & ~0x1f;
|
||||
|
||||
return Pitch;
|
||||
}
|
||||
|
||||
public static int GetBlockWidth(GalImageFormat Format)
|
||||
{
|
||||
return GetImageDescriptor(Format).BlockWidth;
|
||||
}
|
||||
|
||||
public static int GetBlockHeight(GalImageFormat Format)
|
||||
{
|
||||
return GetImageDescriptor(Format).BlockHeight;
|
||||
}
|
||||
|
||||
public static int GetAlignedWidth(GalImage Image)
|
||||
{
|
||||
ImageDescriptor Desc = GetImageDescriptor(Image.Format);
|
||||
|
||||
int AlignMask;
|
||||
|
||||
if (Image.Layout == GalMemoryLayout.BlockLinear)
|
||||
{
|
||||
AlignMask = Image.TileWidth * (64 / Desc.BytesPerPixel) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
AlignMask = (32 / Desc.BytesPerPixel) - 1;
|
||||
}
|
||||
|
||||
return (Image.Width + AlignMask) & ~AlignMask;
|
||||
}
|
||||
|
||||
public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image)
|
||||
{
|
||||
ImageDescriptor Desc = GetImageDescriptor(Image.Format);
|
||||
|
||||
return (DivRoundUp(Image.Width, Desc.BlockWidth),
|
||||
DivRoundUp(Image.Height, Desc.BlockHeight));
|
||||
}
|
||||
|
||||
public static int GetBytesPerPixel(GalImageFormat Format)
|
||||
{
|
||||
return GetImageDescriptor(Format).BytesPerPixel;
|
||||
}
|
||||
|
||||
private static int DivRoundUp(int LHS, int RHS)
|
||||
{
|
||||
return (LHS + (RHS - 1)) / RHS;
|
||||
}
|
||||
|
||||
public static bool HasColor(GalImageFormat Format)
|
||||
{
|
||||
return (GetImageDescriptor(Format).Target & TargetBuffer.Color) != 0;
|
||||
}
|
||||
|
||||
public static bool HasDepth(GalImageFormat Format)
|
||||
{
|
||||
return (GetImageDescriptor(Format).Target & TargetBuffer.Depth) != 0;
|
||||
}
|
||||
|
||||
public static bool HasStencil(GalImageFormat Format)
|
||||
{
|
||||
return (GetImageDescriptor(Format).Target & TargetBuffer.Stencil) != 0;
|
||||
}
|
||||
|
||||
public static bool IsCompressed(GalImageFormat Format)
|
||||
{
|
||||
ImageDescriptor Desc = GetImageDescriptor(Format);
|
||||
|
||||
return (Desc.BlockWidth | Desc.BlockHeight) != 1;
|
||||
}
|
||||
|
||||
private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
|
||||
{
|
||||
GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask;
|
||||
|
||||
if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor))
|
||||
{
|
||||
return Descriptor;
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!");
|
||||
}
|
||||
|
||||
private static GalImageFormat GetFormatType(GalTextureType Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case GalTextureType.Snorm: return Snorm;
|
||||
case GalTextureType.Unorm: return Unorm;
|
||||
case GalTextureType.Sint: return Sint;
|
||||
case GalTextureType.Uint: return Uint;
|
||||
case GalTextureType.Float: return Float;
|
||||
|
||||
default: throw new NotImplementedException(((int)Type).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
269
Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs
Normal file
269
Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs
Normal file
@ -0,0 +1,269 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public struct IntegerEncoded
|
||||
{
|
||||
public enum EIntegerEncoding
|
||||
{
|
||||
JustBits,
|
||||
Quint,
|
||||
Trit
|
||||
}
|
||||
|
||||
EIntegerEncoding Encoding;
|
||||
public int NumberBits { get; private set; }
|
||||
public int BitValue { get; private set; }
|
||||
public int TritValue { get; private set; }
|
||||
public int QuintValue { get; private set; }
|
||||
|
||||
public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
|
||||
{
|
||||
Encoding = _Encoding;
|
||||
NumberBits = NumBits;
|
||||
BitValue = 0;
|
||||
TritValue = 0;
|
||||
QuintValue = 0;
|
||||
}
|
||||
|
||||
public bool MatchesEncoding(IntegerEncoded Other)
|
||||
{
|
||||
return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
|
||||
}
|
||||
|
||||
public EIntegerEncoding GetEncoding()
|
||||
{
|
||||
return Encoding;
|
||||
}
|
||||
|
||||
public int GetBitLength(int NumberVals)
|
||||
{
|
||||
int TotalBits = NumberBits * NumberVals;
|
||||
if (Encoding == EIntegerEncoding.Trit)
|
||||
{
|
||||
TotalBits += (NumberVals * 8 + 4) / 5;
|
||||
}
|
||||
else if (Encoding == EIntegerEncoding.Quint)
|
||||
{
|
||||
TotalBits += (NumberVals * 7 + 2) / 3;
|
||||
}
|
||||
return TotalBits;
|
||||
}
|
||||
|
||||
public static IntegerEncoded CreateEncoding(int MaxVal)
|
||||
{
|
||||
while (MaxVal > 0)
|
||||
{
|
||||
int Check = MaxVal + 1;
|
||||
|
||||
// Is maxVal a power of two?
|
||||
if ((Check & (Check - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 3*2^n - 1?
|
||||
if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
|
||||
}
|
||||
|
||||
// Is maxVal of the type 5*2^n - 1?
|
||||
if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
|
||||
{
|
||||
return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
|
||||
}
|
||||
|
||||
// Apparently it can't be represented with a bounded integer sequence...
|
||||
// just iterate.
|
||||
MaxVal--;
|
||||
}
|
||||
|
||||
return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
|
||||
}
|
||||
|
||||
public static void DecodeTritBlock(
|
||||
BitArrayStream BitStream,
|
||||
List<IntegerEncoded> ListIntegerEncoded,
|
||||
int NumberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[5];
|
||||
int[] t = new int[5];
|
||||
int T;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.14
|
||||
m[0] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T = BitStream.ReadBits(2);
|
||||
m[1] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(2) << 2;
|
||||
m[2] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(1) << 4;
|
||||
m[3] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(2) << 5;
|
||||
m[4] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
T |= BitStream.ReadBits(1) << 7;
|
||||
|
||||
int C = 0;
|
||||
|
||||
BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
|
||||
if (Tb.ReadBits(2, 4) == 7)
|
||||
{
|
||||
C = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
|
||||
t[4] = t[3] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
C = Tb.ReadBits(0, 4);
|
||||
if (Tb.ReadBits(5, 6) == 3)
|
||||
{
|
||||
t[4] = 2;
|
||||
t[3] = Tb.ReadBit(7);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[4] = Tb.ReadBit(7);
|
||||
t[3] = Tb.ReadBits(5, 6);
|
||||
}
|
||||
}
|
||||
|
||||
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
|
||||
if (Cb.ReadBits(0, 1) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = Cb.ReadBit(4);
|
||||
t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
|
||||
}
|
||||
else if (Cb.ReadBits(2, 3) == 3)
|
||||
{
|
||||
t[2] = 2;
|
||||
t[1] = 2;
|
||||
t[0] = Cb.ReadBits(0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[2] = Cb.ReadBit(4);
|
||||
t[1] = Cb.ReadBits(2, 3);
|
||||
t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
TritValue = t[i]
|
||||
};
|
||||
ListIntegerEncoded.Add(IntEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeQuintBlock(
|
||||
BitArrayStream BitStream,
|
||||
List<IntegerEncoded> ListIntegerEncoded,
|
||||
int NumberBitsPerValue)
|
||||
{
|
||||
// Implement the algorithm in section C.2.12
|
||||
int[] m = new int[3];
|
||||
int[] q = new int[3];
|
||||
int Q;
|
||||
|
||||
// Read the trit encoded block according to
|
||||
// table C.2.15
|
||||
m[0] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q = BitStream.ReadBits(3);
|
||||
m[1] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q |= BitStream.ReadBits(2) << 3;
|
||||
m[2] = BitStream.ReadBits(NumberBitsPerValue);
|
||||
Q |= BitStream.ReadBits(2) << 5;
|
||||
|
||||
BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
|
||||
if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
|
||||
{
|
||||
q[0] = q[1] = 4;
|
||||
q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int C = 0;
|
||||
if (Qb.ReadBits(1, 2) == 3)
|
||||
{
|
||||
q[2] = 4;
|
||||
C = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
q[2] = Qb.ReadBits(5, 6);
|
||||
C = Qb.ReadBits(0, 4);
|
||||
}
|
||||
|
||||
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
|
||||
if (Cb.ReadBits(0, 2) == 5)
|
||||
{
|
||||
q[1] = 4;
|
||||
q[0] = Cb.ReadBits(3, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
q[1] = Cb.ReadBits(3, 4);
|
||||
q[0] = Cb.ReadBits(0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
|
||||
{
|
||||
BitValue = m[i],
|
||||
QuintValue = q[i]
|
||||
};
|
||||
ListIntegerEncoded.Add(IntEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeIntegerSequence(
|
||||
List<IntegerEncoded> DecodeIntegerSequence,
|
||||
BitArrayStream BitStream,
|
||||
int MaxRange,
|
||||
int NumberValues)
|
||||
{
|
||||
// Determine encoding parameters
|
||||
IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
|
||||
|
||||
// Start decoding
|
||||
int NumberValuesDecoded = 0;
|
||||
while (NumberValuesDecoded < NumberValues)
|
||||
{
|
||||
switch (IntEncoded.GetEncoding())
|
||||
{
|
||||
case EIntegerEncoding.Quint:
|
||||
{
|
||||
DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
|
||||
NumberValuesDecoded += 3;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.Trit:
|
||||
{
|
||||
DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
|
||||
NumberValuesDecoded += 5;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EIntegerEncoding.JustBits:
|
||||
{
|
||||
IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
|
||||
DecodeIntegerSequence.Add(IntEncoded);
|
||||
NumberValuesDecoded++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs
Normal file
19
Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
class LinearSwizzle : ISwizzle
|
||||
{
|
||||
private int Pitch;
|
||||
private int Bpp;
|
||||
|
||||
public LinearSwizzle(int Pitch, int Bpp)
|
||||
{
|
||||
this.Pitch = Pitch;
|
||||
this.Bpp = Bpp;
|
||||
}
|
||||
|
||||
public int GetSwizzleOffset(int X, int Y)
|
||||
{
|
||||
return X * Bpp + Y * Pitch;
|
||||
}
|
||||
}
|
||||
}
|
117
Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs
Normal file
117
Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
static class TextureFactory
|
||||
{
|
||||
public static GalImage MakeTexture(NvGpuVmm Vmm, long TicPosition)
|
||||
{
|
||||
int[] Tic = ReadWords(Vmm, TicPosition, 8);
|
||||
|
||||
GalImageFormat Format = GetImageFormat(Tic);
|
||||
|
||||
GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
|
||||
GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
|
||||
GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
|
||||
GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
|
||||
|
||||
TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
|
||||
|
||||
GalMemoryLayout Layout;
|
||||
|
||||
if (Swizzle == TextureSwizzle.BlockLinear ||
|
||||
Swizzle == TextureSwizzle.BlockLinearColorKey)
|
||||
{
|
||||
Layout = GalMemoryLayout.BlockLinear;
|
||||
}
|
||||
else
|
||||
{
|
||||
Layout = GalMemoryLayout.Pitch;
|
||||
}
|
||||
|
||||
int BlockHeightLog2 = (Tic[3] >> 3) & 7;
|
||||
int TileWidthLog2 = (Tic[3] >> 10) & 7;
|
||||
|
||||
int BlockHeight = 1 << BlockHeightLog2;
|
||||
int TileWidth = 1 << TileWidthLog2;
|
||||
|
||||
int Width = (Tic[4] & 0xffff) + 1;
|
||||
int Height = (Tic[5] & 0xffff) + 1;
|
||||
|
||||
GalImage Image = new GalImage(
|
||||
Width,
|
||||
Height,
|
||||
TileWidth,
|
||||
BlockHeight,
|
||||
Layout,
|
||||
Format,
|
||||
XSource,
|
||||
YSource,
|
||||
ZSource,
|
||||
WSource);
|
||||
|
||||
if (Layout == GalMemoryLayout.Pitch)
|
||||
{
|
||||
Image.Pitch = (Tic[3] & 0xffff) << 5;
|
||||
}
|
||||
|
||||
return Image;
|
||||
}
|
||||
|
||||
public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
|
||||
{
|
||||
int[] Tsc = ReadWords(Vmm, TscPosition, 8);
|
||||
|
||||
GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
|
||||
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
|
||||
GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
|
||||
|
||||
GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3);
|
||||
GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3);
|
||||
GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
|
||||
|
||||
GalColorF BorderColor = new GalColorF(
|
||||
BitConverter.Int32BitsToSingle(Tsc[4]),
|
||||
BitConverter.Int32BitsToSingle(Tsc[5]),
|
||||
BitConverter.Int32BitsToSingle(Tsc[6]),
|
||||
BitConverter.Int32BitsToSingle(Tsc[7]));
|
||||
|
||||
return new GalTextureSampler(
|
||||
AddressU,
|
||||
AddressV,
|
||||
AddressP,
|
||||
MinFilter,
|
||||
MagFilter,
|
||||
MipFilter,
|
||||
BorderColor);
|
||||
}
|
||||
|
||||
private static GalImageFormat GetImageFormat(int[] Tic)
|
||||
{
|
||||
GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7);
|
||||
GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
|
||||
GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
|
||||
GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);
|
||||
|
||||
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
|
||||
|
||||
bool ConvSrgb = ((Tic[4] >> 22) & 1) != 0;
|
||||
|
||||
return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType, ConvSrgb);
|
||||
}
|
||||
|
||||
private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count)
|
||||
{
|
||||
int[] Words = new int[Count];
|
||||
|
||||
for (int Index = 0; Index < Count; Index++, Position += 4)
|
||||
{
|
||||
Words[Index] = Vmm.ReadInt32(Position);
|
||||
}
|
||||
|
||||
return Words;
|
||||
}
|
||||
}
|
||||
}
|
42
Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs
Normal file
42
Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
static class TextureHelper
|
||||
{
|
||||
public static ISwizzle GetSwizzle(GalImage Image)
|
||||
{
|
||||
int BlockWidth = ImageUtils.GetBlockWidth (Image.Format);
|
||||
int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format);
|
||||
|
||||
int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth;
|
||||
|
||||
if (Image.Layout == GalMemoryLayout.BlockLinear)
|
||||
{
|
||||
int AlignMask = Image.TileWidth * (64 / BytesPerPixel) - 1;
|
||||
|
||||
Width = (Width + AlignMask) & ~AlignMask;
|
||||
|
||||
return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new LinearSwizzle(Image.Pitch, BytesPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
public static (MemoryManager Memory, long Position) GetMemoryAndPosition(
|
||||
IMemory Memory,
|
||||
long Position)
|
||||
{
|
||||
if (Memory is NvGpuVmm Vmm)
|
||||
{
|
||||
return (Vmm.Memory, Vmm.GetPhysicalAddress(Position));
|
||||
}
|
||||
|
||||
return ((MemoryManager)Memory, Position);
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs
Normal file
11
Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public enum TextureSwizzle
|
||||
{
|
||||
_1dBuffer = 0,
|
||||
PitchColorKey = 1,
|
||||
Pitch = 2,
|
||||
BlockLinear = 3,
|
||||
BlockLinearColorKey = 4
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user