ryujinx/Ryujinx.Graphics/NvGpuEngine2d.cs
ReinUsesLisp ce1d5be212 Move GPU emulation from Ryujinx.HLE to Ryujinx.Graphics and misc changes (#402)
* Move GPU LLE emulation from HLE to Graphics

* Graphics: Move Gal/Texture to Texture

* Remove Engines/ directory and namespace

* Use tables for image formats

* Abstract OpCode decoding

* Simplify image table

* Do not leak Read* symbols in TextureReader

* Fixups

* Rename IGalFrameBuffer -> IGalRenderTarget

* Remove MaxBpp hardcoded value

* Change yet again texture data and add G8R8 flipping

* Rename GalFrameBufferFormat to GalSurfaceFormat

* Unident EnsureSetup in ImageHandler

* Add IsCompressed

* Address some feedback
2018-09-08 14:51:50 -03:00

208 lines
6.5 KiB
C#

using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics
{
public class NvGpuEngine2d : INvGpuEngine
{
private enum CopyOperation
{
SrcCopyAnd,
RopAnd,
Blend,
SrcCopy,
Rop,
SrcCopyPremult,
BlendPremult
}
public int[] Registers { get; private set; }
private NvGpu Gpu;
private Dictionary<int, NvGpuMethod> Methods;
public NvGpuEngine2d(NvGpu Gpu)
{
this.Gpu = Gpu;
Registers = new int[0xe00];
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(0xb5, 1, 1, TextureCopy);
}
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
{
Method(Vmm, PBEntry);
}
else
{
WriteRegister(PBEntry);
}
}
private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{
CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
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);
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);
TextureSwizzle SrcSwizzle = SrcLinear
? TextureSwizzle.Pitch
: TextureSwizzle.BlockLinear;
TextureSwizzle DstSwizzle = DstLinear
? TextureSwizzle.Pitch
: TextureSwizzle.BlockLinear;
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);
bool IsSrcFb = Gpu.Engine3d.IsFrameBufferPosition(SrcKey);
bool IsDstFb = Gpu.Engine3d.IsFrameBufferPosition(DstKey);
TextureInfo SrcTexture()
{
return new TextureInfo(
SrcAddress,
SrcWidth,
SrcHeight,
SrcPitch,
SrcBlockHeight, 1,
SrcSwizzle,
GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
}
TextureInfo DstTexture()
{
return new TextureInfo(
DstAddress,
DstWidth,
DstHeight,
DstPitch,
DstBlockHeight, 1,
DstSwizzle,
GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
}
//TODO: fb -> fb copies, tex -> fb copies, formats other than RGBA8,
//make it throw for unimpl stuff (like the copy mode)...
if (IsSrcFb && IsDstFb)
{
//Frame Buffer -> Frame Buffer copy.
Gpu.Renderer.RenderTarget.Copy(
SrcKey,
DstKey,
0,
0,
SrcWidth,
SrcHeight,
0,
0,
DstWidth,
DstHeight);
}
if (IsSrcFb)
{
//Frame Buffer -> Texture copy.
Gpu.Renderer.RenderTarget.GetBufferData(SrcKey, (byte[] Buffer) =>
{
TextureInfo Src = SrcTexture();
TextureInfo Dst = DstTexture();
if (Src.Width != Dst.Width ||
Src.Height != Dst.Height)
{
throw new NotImplementedException("Texture resizing is not supported");
}
TextureWriter.Write(Vmm, Dst, Buffer);
});
}
else if (IsDstFb)
{
byte[] Buffer = TextureReader.Read(Vmm, SrcTexture());
Gpu.Renderer.RenderTarget.SetBufferData(
DstKey,
DstWidth,
DstHeight,
Buffer);
}
else
{
//Texture -> Texture copy.
TextureInfo Src = SrcTexture();
TextureInfo Dst = DstTexture();
if (Src.Width != Dst.Width ||
Src.Height != Dst.Height)
{
throw new NotImplementedException("Texture resizing is not supported");
}
TextureWriter.Write(Vmm, Dst, TextureReader.Read(Vmm, Src));
}
}
private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg)
{
return
(long)Registers[(int)Reg + 0] << 32 |
(uint)Registers[(int)Reg + 1];
}
private void WriteRegister(NvGpuPBEntry PBEntry)
{
int ArgsCount = PBEntry.Arguments.Count;
if (ArgsCount > 0)
{
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
}
}
private int ReadRegister(NvGpuEngine2dReg Reg)
{
return Registers[(int)Reg];
}
private void WriteRegister(NvGpuEngine2dReg Reg, int Value)
{
Registers[(int)Reg] = Value;
}
}
}