mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-12-22 23:45:46 +00:00
Fix for current framebuffer issues (#78)
[GPU] Fix some of the current framebuffer issues
This commit is contained in:
parent
262b5b8054
commit
c8c86a3854
23 changed files with 482 additions and 891 deletions
|
@ -244,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
|
|
||||||
Context.Memory.WriteInt64(Position + 0x20, Offset);
|
Context.Memory.WriteInt64(Position + 0x20, Offset);
|
||||||
|
|
||||||
|
Map.GpuAddress = Offset;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
|
||||||
public int Align;
|
public int Align;
|
||||||
public int Kind;
|
public int Kind;
|
||||||
public long CpuAddress;
|
public long CpuAddress;
|
||||||
|
public long GpuAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ using ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using Ryujinx.Core.OsHle.Services.Nv;
|
using Ryujinx.Core.OsHle.Services.Nv;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using Ryujinx.Graphics.Gpu;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -63,13 +64,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
|
|
||||||
private ManualResetEvent WaitBufferFree;
|
private ManualResetEvent WaitBufferFree;
|
||||||
|
|
||||||
private object RenderQueueLock;
|
private bool Disposed;
|
||||||
|
|
||||||
private int RenderQueueCount;
|
|
||||||
|
|
||||||
private bool NvFlingerDisposed;
|
|
||||||
|
|
||||||
private bool KeepRunning;
|
|
||||||
|
|
||||||
public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
|
public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
|
||||||
{
|
{
|
||||||
|
@ -92,10 +87,6 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
BufferQueue = new BufferEntry[0x40];
|
BufferQueue = new BufferEntry[0x40];
|
||||||
|
|
||||||
WaitBufferFree = new ManualResetEvent(false);
|
WaitBufferFree = new ManualResetEvent(false);
|
||||||
|
|
||||||
RenderQueueLock = new object();
|
|
||||||
|
|
||||||
KeepRunning = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
|
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
|
||||||
|
@ -285,35 +276,24 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
|
private void SendFrameBuffer(ServiceCtx Context, int Slot)
|
||||||
{
|
{
|
||||||
int FbWidth = BufferQueue[Slot].Data.Width;
|
int FbWidth = 1280;
|
||||||
int FbHeight = BufferQueue[Slot].Data.Height;
|
int FbHeight = 720;
|
||||||
|
|
||||||
long FbSize = (uint)FbWidth * FbHeight * 4;
|
|
||||||
|
|
||||||
NvMap Map = GetNvMap(Context, Slot);
|
NvMap Map = GetNvMap(Context, Slot);
|
||||||
|
|
||||||
NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
|
NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
|
||||||
|
|
||||||
long Address = Map.CpuAddress;
|
long CpuAddr = Map.CpuAddress;
|
||||||
|
long GpuAddr = Map.GpuAddress;
|
||||||
|
|
||||||
if (MapFb.HasBufferOffset(Slot))
|
if (MapFb.HasBufferOffset(Slot))
|
||||||
{
|
{
|
||||||
Address += MapFb.GetBufferOffset(Slot);
|
CpuAddr += MapFb.GetBufferOffset(Slot);
|
||||||
}
|
|
||||||
|
|
||||||
if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize)
|
//TODO: Enable once the frame buffers problems are fixed.
|
||||||
{
|
//GpuAddr += MapFb.GetBufferOffset(Slot);
|
||||||
Logging.Error($"Frame buffer address {Address:x16} is invalid!");
|
|
||||||
|
|
||||||
BufferQueue[Slot].State = BufferState.Free;
|
|
||||||
|
|
||||||
ReleaseEvent.Handle.Set();
|
|
||||||
|
|
||||||
WaitBufferFree.Set();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQueue[Slot].State = BufferState.Acquired;
|
BufferQueue[Slot].State = BufferState.Acquired;
|
||||||
|
@ -367,41 +347,28 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
Rotate = -MathF.PI * 0.5f;
|
Rotate = -MathF.PI * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (RenderQueueLock)
|
Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
|
||||||
{
|
|
||||||
if (NvFlingerDisposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Interlocked.Increment(ref RenderQueueCount);
|
//TODO: Support double buffering here aswell, it is broken for GPU
|
||||||
|
//frame buffers because it seems to be completely out of sync.
|
||||||
|
if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr))
|
||||||
|
{
|
||||||
|
//Frame buffer is rendered to by the GPU, we can just
|
||||||
|
//bind the frame buffer texture, it's not necessary to read anything.
|
||||||
|
Renderer.SetFrameBuffer(GpuAddr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Frame buffer is not set on the GPU registers, in this case
|
||||||
|
//assume that the app is manually writing to it.
|
||||||
|
Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight);
|
||||||
|
|
||||||
|
byte[] Data = TextureReader.Read(Context.Memory, Texture);
|
||||||
|
|
||||||
|
Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte* Fb = (byte*)Context.Memory.Ram + Address;
|
Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
|
||||||
|
|
||||||
Context.Ns.Gpu.Renderer.QueueAction(delegate()
|
|
||||||
{
|
|
||||||
Context.Ns.Gpu.Renderer.SetFrameBuffer(
|
|
||||||
Fb,
|
|
||||||
FbWidth,
|
|
||||||
FbHeight,
|
|
||||||
ScaleX,
|
|
||||||
ScaleY,
|
|
||||||
OffsX,
|
|
||||||
OffsY,
|
|
||||||
Rotate);
|
|
||||||
|
|
||||||
BufferQueue[Slot].State = BufferState.Free;
|
|
||||||
|
|
||||||
Interlocked.Decrement(ref RenderQueueCount);
|
|
||||||
|
|
||||||
ReleaseEvent.Handle.Set();
|
|
||||||
|
|
||||||
lock (WaitBufferFree)
|
|
||||||
{
|
|
||||||
WaitBufferFree.Set();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private NvMap GetNvMap(ServiceCtx Context, int Slot)
|
private NvMap GetNvMap(ServiceCtx Context, int Slot)
|
||||||
|
@ -420,6 +387,18 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
|
return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReleaseBuffer(int Slot)
|
||||||
|
{
|
||||||
|
BufferQueue[Slot].State = BufferState.Free;
|
||||||
|
|
||||||
|
ReleaseEvent.Handle.Set();
|
||||||
|
|
||||||
|
lock (WaitBufferFree)
|
||||||
|
{
|
||||||
|
WaitBufferFree.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int GetFreeSlotBlocking(int Width, int Height)
|
private int GetFreeSlotBlocking(int Width, int Height)
|
||||||
{
|
{
|
||||||
int Slot;
|
int Slot;
|
||||||
|
@ -435,7 +414,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
|
|
||||||
Logging.Debug("Waiting for a free BufferQueue slot...");
|
Logging.Debug("Waiting for a free BufferQueue slot...");
|
||||||
|
|
||||||
if (!KeepRunning)
|
if (Disposed)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -445,7 +424,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
|
|
||||||
WaitBufferFree.WaitOne();
|
WaitBufferFree.WaitOne();
|
||||||
}
|
}
|
||||||
while (KeepRunning);
|
while (!Disposed);
|
||||||
|
|
||||||
Logging.Debug($"Found free BufferQueue slot {Slot}!");
|
Logging.Debug($"Found free BufferQueue slot {Slot}!");
|
||||||
|
|
||||||
|
@ -485,26 +464,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
|
||||||
|
|
||||||
protected virtual void Dispose(bool Disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
if (Disposing && !NvFlingerDisposed)
|
if (Disposing && !Disposed)
|
||||||
{
|
{
|
||||||
lock (RenderQueueLock)
|
Disposed = true;
|
||||||
{
|
|
||||||
NvFlingerDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Ensure that all pending actions was sent before
|
|
||||||
//we can safely assume that the class was disposed.
|
|
||||||
while (RenderQueueCount > 0)
|
|
||||||
{
|
|
||||||
Thread.Yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
Renderer.ResetFrameBuffer();
|
|
||||||
|
|
||||||
lock (WaitBufferFree)
|
lock (WaitBufferFree)
|
||||||
{
|
{
|
||||||
KeepRunning = false;
|
|
||||||
|
|
||||||
WaitBufferFree.Set();
|
WaitBufferFree.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
public enum GalBlendEquation
|
public enum GalBlendEquation
|
||||||
{
|
{
|
||||||
FuncAdd = 0x8006,
|
FuncAdd = 1,
|
||||||
Min = 0x8007,
|
FuncSubtract = 2,
|
||||||
Max = 0x8008,
|
FuncReverseSubtract = 3,
|
||||||
FuncSubtract = 0x800a,
|
Min = 4,
|
||||||
FuncReverseSubtract = 0x800b
|
Max = 5
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,24 +2,24 @@ namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
public enum GalBlendFactor
|
public enum GalBlendFactor
|
||||||
{
|
{
|
||||||
Zero = 0x4000,
|
Zero = 0x1,
|
||||||
One = 0x4001,
|
One = 0x2,
|
||||||
SrcColor = 0x4300,
|
SrcColor = 0x3,
|
||||||
OneMinusSrcColor = 0x4301,
|
OneMinusSrcColor = 0x4,
|
||||||
SrcAlpha = 0x4302,
|
SrcAlpha = 0x5,
|
||||||
OneMinusSrcAlpha = 0x4303,
|
OneMinusSrcAlpha = 0x6,
|
||||||
DstAlpha = 0x4304,
|
DstAlpha = 0x7,
|
||||||
OneMinusDstAlpha = 0x4305,
|
OneMinusDstAlpha = 0x8,
|
||||||
DstColor = 0x4306,
|
DstColor = 0x9,
|
||||||
OneMinusDstColor = 0x4307,
|
OneMinusDstColor = 0xa,
|
||||||
SrcAlphaSaturate = 0x4308,
|
SrcAlphaSaturate = 0xb,
|
||||||
ConstantColor = 0xc001,
|
Src1Color = 0x10,
|
||||||
OneMinusConstantColor = 0xc002,
|
OneMinusSrc1Color = 0x11,
|
||||||
ConstantAlpha = 0xc003,
|
Src1Alpha = 0x12,
|
||||||
OneMinusConstantAlpha = 0xc004,
|
OneMinusSrc1Alpha = 0x13,
|
||||||
Src1Color = 0xc900,
|
ConstantColor = 0x61,
|
||||||
OneMinusSrc1Color = 0xc901,
|
OneMinusConstantColor = 0x62,
|
||||||
Src1Alpha = 0xc902,
|
ConstantAlpha = 0x63,
|
||||||
OneMinusSrc1Alpha = 0xc903
|
OneMinusConstantAlpha = 0x64
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,21 +6,12 @@ namespace Ryujinx.Graphics.Gal
|
||||||
public unsafe interface IGalRenderer
|
public unsafe interface IGalRenderer
|
||||||
{
|
{
|
||||||
void QueueAction(Action ActionMthd);
|
void QueueAction(Action ActionMthd);
|
||||||
|
|
||||||
void RunActions();
|
void RunActions();
|
||||||
|
|
||||||
void InitializeFrameBuffer();
|
|
||||||
void ResetFrameBuffer();
|
|
||||||
void Render();
|
void Render();
|
||||||
|
|
||||||
void SetWindowSize(int Width, int Height);
|
void SetWindowSize(int Width, int Height);
|
||||||
void SetFrameBuffer(
|
|
||||||
byte* Fb,
|
|
||||||
int Width,
|
|
||||||
int Height,
|
|
||||||
float ScaleX,
|
|
||||||
float ScaleY,
|
|
||||||
float OffsX,
|
|
||||||
float OffsY,
|
|
||||||
float Rotate);
|
|
||||||
|
|
||||||
//Blend
|
//Blend
|
||||||
void SetBlendEnable(bool Enable);
|
void SetBlendEnable(bool Enable);
|
||||||
|
@ -39,11 +30,17 @@ namespace Ryujinx.Graphics.Gal
|
||||||
GalBlendFactor FuncDstAlpha);
|
GalBlendFactor FuncDstAlpha);
|
||||||
|
|
||||||
//Frame Buffer
|
//Frame Buffer
|
||||||
void SetFb(int FbIndex, int Width, int Height);
|
void CreateFrameBuffer(long Tag, int Width, int Height);
|
||||||
|
|
||||||
void BindFrameBuffer(int FbIndex);
|
void BindFrameBuffer(long Tag);
|
||||||
|
|
||||||
void DrawFrameBuffer(int FbIndex);
|
void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler);
|
||||||
|
|
||||||
|
void SetFrameBuffer(long Tag);
|
||||||
|
|
||||||
|
void SetFrameBuffer(byte[] Data, int Width, int Height);
|
||||||
|
|
||||||
|
void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
|
||||||
|
|
||||||
//Rasterizer
|
//Rasterizer
|
||||||
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
|
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
|
||||||
|
@ -70,8 +67,8 @@ namespace Ryujinx.Graphics.Gal
|
||||||
void BindProgram();
|
void BindProgram();
|
||||||
|
|
||||||
//Texture
|
//Texture
|
||||||
void SetTexture(int Index, GalTexture Tex);
|
void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
|
||||||
|
|
||||||
void SetSampler(int Index, GalTextureSampler Sampler);
|
void BindTexture(int Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,250 +0,0 @@
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
|
||||||
{
|
|
||||||
unsafe class FrameBuffer
|
|
||||||
{
|
|
||||||
public int WindowWidth { get; set; }
|
|
||||||
public int WindowHeight { get; set; }
|
|
||||||
|
|
||||||
private int VtxShaderHandle;
|
|
||||||
private int FragShaderHandle;
|
|
||||||
private int PrgShaderHandle;
|
|
||||||
|
|
||||||
private int TexHandle;
|
|
||||||
private int TexWidth;
|
|
||||||
private int TexHeight;
|
|
||||||
|
|
||||||
private int VaoHandle;
|
|
||||||
private int VboHandle;
|
|
||||||
|
|
||||||
private int[] Pixels;
|
|
||||||
|
|
||||||
private byte* FbPtr;
|
|
||||||
|
|
||||||
private object FbPtrLock;
|
|
||||||
|
|
||||||
public FrameBuffer(int Width, int Height)
|
|
||||||
{
|
|
||||||
if (Width < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Width));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Height < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
|
||||||
}
|
|
||||||
|
|
||||||
FbPtrLock = new object();
|
|
||||||
|
|
||||||
TexWidth = Width;
|
|
||||||
TexHeight = Height;
|
|
||||||
|
|
||||||
WindowWidth = Width;
|
|
||||||
WindowHeight = Height;
|
|
||||||
|
|
||||||
SetupShaders();
|
|
||||||
SetupTexture();
|
|
||||||
SetupVertex();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupShaders()
|
|
||||||
{
|
|
||||||
VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader);
|
|
||||||
FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
|
|
||||||
|
|
||||||
string VtxShaderSource = EmbeddedResource.GetString("GlFbVtxShader");
|
|
||||||
string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader");
|
|
||||||
|
|
||||||
GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
|
|
||||||
GL.ShaderSource(FragShaderHandle, FragShaderSource);
|
|
||||||
GL.CompileShader(VtxShaderHandle);
|
|
||||||
GL.CompileShader(FragShaderHandle);
|
|
||||||
|
|
||||||
PrgShaderHandle = GL.CreateProgram();
|
|
||||||
|
|
||||||
GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
|
|
||||||
GL.AttachShader(PrgShaderHandle, FragShaderHandle);
|
|
||||||
GL.LinkProgram(PrgShaderHandle);
|
|
||||||
GL.UseProgram(PrgShaderHandle);
|
|
||||||
|
|
||||||
int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
|
|
||||||
|
|
||||||
GL.Uniform1(TexUniformLocation, 0);
|
|
||||||
|
|
||||||
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
|
|
||||||
|
|
||||||
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupTexture()
|
|
||||||
{
|
|
||||||
Pixels = new int[TexWidth * TexHeight];
|
|
||||||
|
|
||||||
if (TexHandle == 0)
|
|
||||||
{
|
|
||||||
TexHandle = GL.GenTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
||||||
GL.TexImage2D(TextureTarget.Texture2D,
|
|
||||||
0,
|
|
||||||
PixelInternalFormat.Rgba,
|
|
||||||
TexWidth,
|
|
||||||
TexHeight,
|
|
||||||
0,
|
|
||||||
PixelFormat.Rgba,
|
|
||||||
PixelType.UnsignedByte,
|
|
||||||
IntPtr.Zero);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupVertex()
|
|
||||||
{
|
|
||||||
VaoHandle = GL.GenVertexArray();
|
|
||||||
VboHandle = GL.GenBuffer();
|
|
||||||
|
|
||||||
float[] Buffer = new float[]
|
|
||||||
{
|
|
||||||
-1, 1, 0, 0,
|
|
||||||
1, 1, 1, 0,
|
|
||||||
-1, -1, 0, 1,
|
|
||||||
1, -1, 1, 1
|
|
||||||
};
|
|
||||||
|
|
||||||
IntPtr Length = new IntPtr(Buffer.Length * 4);
|
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
||||||
|
|
||||||
GL.BindVertexArray(VaoHandle);
|
|
||||||
|
|
||||||
GL.EnableVertexAttribArray(0);
|
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
||||||
|
|
||||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
|
|
||||||
|
|
||||||
GL.EnableVertexAttribArray(1);
|
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
||||||
|
|
||||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
|
|
||||||
|
|
||||||
GL.BindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
|
|
||||||
{
|
|
||||||
if (Fb == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Fb));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Width < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Width));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Height < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (FbPtrLock)
|
|
||||||
{
|
|
||||||
FbPtr = Fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Width != TexWidth ||
|
|
||||||
Height != TexHeight)
|
|
||||||
{
|
|
||||||
TexWidth = Width;
|
|
||||||
TexHeight = Height;
|
|
||||||
|
|
||||||
SetupTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.UseProgram(PrgShaderHandle);
|
|
||||||
|
|
||||||
int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform");
|
|
||||||
|
|
||||||
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
|
|
||||||
|
|
||||||
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
|
|
||||||
|
|
||||||
GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight));
|
|
||||||
|
|
||||||
int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
|
|
||||||
|
|
||||||
GL.Uniform2(OffsetUniformLocation, Offs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
lock (FbPtrLock)
|
|
||||||
{
|
|
||||||
FbPtr = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render()
|
|
||||||
{
|
|
||||||
lock (FbPtrLock)
|
|
||||||
{
|
|
||||||
if (FbPtr == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int Y = 0; Y < TexHeight; Y++)
|
|
||||||
for (int X = 0; X < TexWidth; X++)
|
|
||||||
{
|
|
||||||
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
|
||||||
GL.TexSubImage2D(TextureTarget.Texture2D,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
TexWidth,
|
|
||||||
TexHeight,
|
|
||||||
PixelFormat.Rgba,
|
|
||||||
PixelType.UnsignedByte,
|
|
||||||
Pixels);
|
|
||||||
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
|
||||||
|
|
||||||
GL.BindVertexArray(VaoHandle);
|
|
||||||
|
|
||||||
GL.UseProgram(PrgShaderHandle);
|
|
||||||
|
|
||||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetSwizzleOffset(int X, int Y)
|
|
||||||
{
|
|
||||||
int Pos;
|
|
||||||
|
|
||||||
Pos = (Y & 0x7f) >> 4;
|
|
||||||
Pos += (X >> 4) << 3;
|
|
||||||
Pos += (Y >> 7) * ((TexWidth >> 4) << 3);
|
|
||||||
Pos *= 1024;
|
|
||||||
Pos += ((Y & 0xf) >> 3) << 9;
|
|
||||||
Pos += ((X & 0xf) >> 3) << 8;
|
|
||||||
Pos += ((Y & 0x7) >> 1) << 6;
|
|
||||||
Pos += ((X & 0x7) >> 2) << 5;
|
|
||||||
Pos += ((Y & 0x1) >> 0) << 4;
|
|
||||||
Pos += ((X & 0x3) >> 0) << 2;
|
|
||||||
|
|
||||||
return Pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -127,17 +127,72 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
|
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
|
||||||
{
|
{
|
||||||
return (BlendEquationMode)BlendEquation;
|
switch (BlendEquation)
|
||||||
|
{
|
||||||
|
case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd;
|
||||||
|
case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract;
|
||||||
|
case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
|
||||||
|
case GalBlendEquation.Min: return BlendEquationMode.Min;
|
||||||
|
case GalBlendEquation.Max: return BlendEquationMode.Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(BlendEquation));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
|
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
|
||||||
{
|
{
|
||||||
return (BlendingFactorSrc)(BlendFactor - 0x4000);
|
switch (BlendFactor)
|
||||||
|
{
|
||||||
|
case GalBlendFactor.Zero: return BlendingFactorSrc.Zero;
|
||||||
|
case GalBlendFactor.One: return BlendingFactorSrc.One;
|
||||||
|
case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor;
|
||||||
|
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorSrc.OneMinusSrcColor;
|
||||||
|
case GalBlendFactor.DstColor: return BlendingFactorSrc.DstColor;
|
||||||
|
case GalBlendFactor.OneMinusDstColor: return BlendingFactorSrc.OneMinusDstColor;
|
||||||
|
case GalBlendFactor.SrcAlpha: return BlendingFactorSrc.SrcAlpha;
|
||||||
|
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorSrc.OneMinusSrcAlpha;
|
||||||
|
case GalBlendFactor.DstAlpha: return BlendingFactorSrc.DstAlpha;
|
||||||
|
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorSrc.OneMinusDstAlpha;
|
||||||
|
case GalBlendFactor.ConstantColor: return BlendingFactorSrc.ConstantColor;
|
||||||
|
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor;
|
||||||
|
case GalBlendFactor.ConstantAlpha: return BlendingFactorSrc.ConstantAlpha;
|
||||||
|
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha;
|
||||||
|
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorSrc.SrcAlphaSaturate;
|
||||||
|
case GalBlendFactor.Src1Color: return BlendingFactorSrc.Src1Color;
|
||||||
|
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorSrc.OneMinusSrc1Color;
|
||||||
|
case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha;
|
||||||
|
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(BlendFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
|
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
|
||||||
{
|
{
|
||||||
return (BlendingFactorDest)(BlendFactor - 0x4000);
|
switch (BlendFactor)
|
||||||
|
{
|
||||||
|
case GalBlendFactor.Zero: return BlendingFactorDest.Zero;
|
||||||
|
case GalBlendFactor.One: return BlendingFactorDest.One;
|
||||||
|
case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor;
|
||||||
|
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorDest.OneMinusSrcColor;
|
||||||
|
case GalBlendFactor.DstColor: return BlendingFactorDest.DstColor;
|
||||||
|
case GalBlendFactor.OneMinusDstColor: return BlendingFactorDest.OneMinusDstColor;
|
||||||
|
case GalBlendFactor.SrcAlpha: return BlendingFactorDest.SrcAlpha;
|
||||||
|
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorDest.OneMinusSrcAlpha;
|
||||||
|
case GalBlendFactor.DstAlpha: return BlendingFactorDest.DstAlpha;
|
||||||
|
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorDest.OneMinusDstAlpha;
|
||||||
|
case GalBlendFactor.ConstantColor: return BlendingFactorDest.ConstantColor;
|
||||||
|
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor;
|
||||||
|
case GalBlendFactor.ConstantAlpha: return BlendingFactorDest.ConstantAlpha;
|
||||||
|
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha;
|
||||||
|
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorDest.SrcAlphaSaturate;
|
||||||
|
case GalBlendFactor.Src1Color: return BlendingFactorDest.Src1Color;
|
||||||
|
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorDest.OneMinusSrc1Color;
|
||||||
|
case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha;
|
||||||
|
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(BlendFactor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,30 @@
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
class OGLFrameBuffer
|
class OGLFrameBuffer
|
||||||
{
|
{
|
||||||
private struct FrameBuffer
|
private class FrameBuffer
|
||||||
{
|
{
|
||||||
public int FbHandle;
|
public int Width { get; set; }
|
||||||
public int RbHandle;
|
public int Height { get; set; }
|
||||||
public int TexHandle;
|
|
||||||
|
public int Handle { get; private set; }
|
||||||
|
public int RbHandle { get; private set; }
|
||||||
|
public int TexHandle { get; private set; }
|
||||||
|
|
||||||
|
public FrameBuffer(int Width, int Height)
|
||||||
|
{
|
||||||
|
this.Width = Width;
|
||||||
|
this.Height = Height;
|
||||||
|
|
||||||
|
Handle = GL.GenFramebuffer();
|
||||||
|
RbHandle = GL.GenRenderbuffer();
|
||||||
|
TexHandle = GL.GenTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ShaderProgram
|
private struct ShaderProgram
|
||||||
|
@ -20,83 +34,175 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
public int FpHandle;
|
public int FpHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FrameBuffer[] Fbs;
|
private Dictionary<long, FrameBuffer> Fbs;
|
||||||
|
|
||||||
private ShaderProgram Shader;
|
private ShaderProgram Shader;
|
||||||
|
|
||||||
private bool IsInitialized;
|
private bool IsInitialized;
|
||||||
|
|
||||||
|
private int RawFbTexWidth;
|
||||||
|
private int RawFbTexHeight;
|
||||||
|
private int RawFbTexHandle;
|
||||||
|
|
||||||
|
private int CurrFbHandle;
|
||||||
|
private int CurrTexHandle;
|
||||||
|
|
||||||
private int VaoHandle;
|
private int VaoHandle;
|
||||||
private int VboHandle;
|
private int VboHandle;
|
||||||
|
|
||||||
public OGLFrameBuffer()
|
public OGLFrameBuffer()
|
||||||
{
|
{
|
||||||
Fbs = new FrameBuffer[16];
|
Fbs = new Dictionary<long, FrameBuffer>();
|
||||||
|
|
||||||
Shader = new ShaderProgram();
|
Shader = new ShaderProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(int Index, int Width, int Height)
|
public void Create(long Tag, int Width, int Height)
|
||||||
{
|
{
|
||||||
if (Fbs[Index].FbHandle != 0)
|
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
|
||||||
{
|
{
|
||||||
|
if (Fb.Width != Width ||
|
||||||
|
Fb.Height != Height)
|
||||||
|
{
|
||||||
|
SetupTexture(Fb.TexHandle, Width, Height);
|
||||||
|
|
||||||
|
Fb.Width = Width;
|
||||||
|
Fb.Height = Height;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fbs[Index].FbHandle = GL.GenFramebuffer();
|
Fb = new FrameBuffer(Width, Height);
|
||||||
Fbs[Index].RbHandle = GL.GenRenderbuffer();
|
|
||||||
Fbs[Index].TexHandle = GL.GenTexture();
|
|
||||||
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
|
SetupTexture(Fb.TexHandle, Width, Height);
|
||||||
|
|
||||||
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
|
||||||
|
|
||||||
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720);
|
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
|
||||||
|
|
||||||
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
|
GL.RenderbufferStorage(
|
||||||
|
RenderbufferTarget.Renderbuffer,
|
||||||
|
RenderbufferStorage.Depth24Stencil8,
|
||||||
|
Width,
|
||||||
|
Height);
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
|
GL.FramebufferRenderbuffer(
|
||||||
|
FramebufferTarget.Framebuffer,
|
||||||
|
FramebufferAttachment.DepthStencilAttachment,
|
||||||
|
RenderbufferTarget.Renderbuffer,
|
||||||
|
Fb.RbHandle);
|
||||||
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
GL.FramebufferTexture(
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
FramebufferTarget.Framebuffer,
|
||||||
|
FramebufferAttachment.ColorAttachment0,
|
||||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
Fb.TexHandle,
|
||||||
|
0);
|
||||||
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0);
|
|
||||||
|
|
||||||
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
||||||
|
|
||||||
|
Fbs.Add(Tag, Fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Bind(int Index)
|
public void Bind(long Tag)
|
||||||
{
|
{
|
||||||
if (Fbs[Index].FbHandle == 0)
|
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
|
||||||
{
|
{
|
||||||
return;
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
|
||||||
}
|
|
||||||
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
|
CurrFbHandle = Fb.Handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(int Index)
|
public void BindTexture(long Tag, int Index)
|
||||||
{
|
{
|
||||||
if (Fbs[Index].FbHandle == 0)
|
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
|
||||||
{
|
{
|
||||||
return;
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(long Tag)
|
||||||
|
{
|
||||||
|
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
|
||||||
|
{
|
||||||
|
CurrTexHandle = Fb.TexHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(byte[] Data, int Width, int Height)
|
||||||
|
{
|
||||||
|
if (RawFbTexHandle == 0)
|
||||||
|
{
|
||||||
|
RawFbTexHandle = GL.GenTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureInitialized();
|
if (RawFbTexWidth != Width ||
|
||||||
|
RawFbTexHeight != Height)
|
||||||
|
{
|
||||||
|
SetupTexture(RawFbTexHandle, Width, Height);
|
||||||
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
RawFbTexWidth = Width;
|
||||||
|
RawFbTexHeight = Height;
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
|
}
|
||||||
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
|
||||||
GL.BindVertexArray(VaoHandle);
|
GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle);
|
||||||
|
|
||||||
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
||||||
|
|
||||||
|
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
|
||||||
|
|
||||||
|
CurrTexHandle = RawFbTexHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTransform(Matrix2 Transform, Vector2 Offs)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
|
||||||
GL.UseProgram(Shader.Handle);
|
GL.UseProgram(Shader.Handle);
|
||||||
|
|
||||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
|
||||||
|
|
||||||
|
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
|
||||||
|
|
||||||
|
int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset");
|
||||||
|
|
||||||
|
GL.Uniform2(OffsetUniformLocation, ref Offs);
|
||||||
|
|
||||||
|
GL.UseProgram(CurrentProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
if (CurrTexHandle != 0)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle);
|
||||||
|
|
||||||
|
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
||||||
|
|
||||||
|
GL.BindVertexArray(VaoHandle);
|
||||||
|
|
||||||
|
GL.UseProgram(Shader.Handle);
|
||||||
|
|
||||||
|
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
||||||
|
|
||||||
|
//Restore the original state.
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle);
|
||||||
|
|
||||||
|
GL.UseProgram(CurrentProgram);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureInitialized()
|
private void EnsureInitialized()
|
||||||
|
@ -130,7 +236,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.LinkProgram(Shader.Handle);
|
GL.LinkProgram(Shader.Handle);
|
||||||
GL.UseProgram(Shader.Handle);
|
GL.UseProgram(Shader.Handle);
|
||||||
|
|
||||||
Matrix2 Transform = Matrix2.CreateScale(1, -1);
|
Matrix2 Transform = Matrix2.Identity;
|
||||||
|
|
||||||
int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
|
int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
|
||||||
|
|
||||||
|
@ -178,5 +284,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetupTexture(int Handle, int Width, int Height)
|
||||||
|
{
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||||
|
|
||||||
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
|
||||||
|
|
||||||
|
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
|
||||||
|
|
||||||
|
const int Level = 0;
|
||||||
|
const int Border = 0;
|
||||||
|
|
||||||
|
GL.TexImage2D(
|
||||||
|
TextureTarget.Texture2D,
|
||||||
|
Level,
|
||||||
|
InternalFmt,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
Border,
|
||||||
|
Format,
|
||||||
|
Type,
|
||||||
|
IntPtr.Zero);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,10 +15,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
||||||
|
|
||||||
int Handle = EnsureTextureInitialized(Index);
|
Bind(Index);
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
|
||||||
|
|
||||||
|
const int Level = 0; //TODO: Support mipmap textures.
|
||||||
const int Border = 0;
|
const int Border = 0;
|
||||||
|
|
||||||
if (IsCompressedTextureFormat(Texture.Format))
|
if (IsCompressedTextureFormat(Texture.Format))
|
||||||
|
@ -27,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.CompressedTexImage2D(
|
GL.CompressedTexImage2D(
|
||||||
TextureTarget.Texture2D,
|
TextureTarget.Texture2D,
|
||||||
0,
|
Level,
|
||||||
InternalFmt,
|
InternalFmt,
|
||||||
Texture.Width,
|
Texture.Width,
|
||||||
Texture.Height,
|
Texture.Height,
|
||||||
|
@ -39,27 +38,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
|
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
|
||||||
|
|
||||||
(PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format);
|
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
|
||||||
|
|
||||||
GL.TexImage2D(
|
GL.TexImage2D(
|
||||||
TextureTarget.Texture2D,
|
TextureTarget.Texture2D,
|
||||||
0,
|
Level,
|
||||||
InternalFmt,
|
InternalFmt,
|
||||||
Texture.Width,
|
Texture.Width,
|
||||||
Texture.Height,
|
Texture.Height,
|
||||||
Border,
|
Border,
|
||||||
Format.Item1,
|
Format,
|
||||||
Format.Item2,
|
Type,
|
||||||
Texture.Data);
|
Texture.Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(int Index, GalTextureSampler Sampler)
|
public void Bind(int Index)
|
||||||
{
|
{
|
||||||
int Handle = EnsureTextureInitialized(Index);
|
int Handle = EnsureTextureInitialized(Index);
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Set(GalTextureSampler Sampler)
|
||||||
|
{
|
||||||
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
|
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
|
||||||
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
|
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private ConcurrentQueue<Action> ActionsQueue;
|
private ConcurrentQueue<Action> ActionsQueue;
|
||||||
|
|
||||||
private FrameBuffer FbRenderer;
|
|
||||||
|
|
||||||
public OpenGLRenderer()
|
public OpenGLRenderer()
|
||||||
{
|
{
|
||||||
Blend = new OGLBlend();
|
Blend = new OGLBlend();
|
||||||
|
@ -36,16 +34,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue = new ConcurrentQueue<Action>();
|
ActionsQueue = new ConcurrentQueue<Action>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeFrameBuffer()
|
|
||||||
{
|
|
||||||
FbRenderer = new FrameBuffer(1280, 720);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetFrameBuffer()
|
|
||||||
{
|
|
||||||
FbRenderer.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void QueueAction(Action ActionMthd)
|
public void QueueAction(Action ActionMthd)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(ActionMthd);
|
ActionsQueue.Enqueue(ActionMthd);
|
||||||
|
@ -63,33 +51,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
FbRenderer.Render();
|
FrameBuffer.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetWindowSize(int Width, int Height)
|
public void SetWindowSize(int Width, int Height)
|
||||||
{
|
{
|
||||||
FbRenderer.WindowWidth = Width;
|
//TODO
|
||||||
FbRenderer.WindowHeight = Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void SetFrameBuffer(
|
|
||||||
byte* Fb,
|
|
||||||
int Width,
|
|
||||||
int Height,
|
|
||||||
float ScaleX,
|
|
||||||
float ScaleY,
|
|
||||||
float OffsX,
|
|
||||||
float OffsY,
|
|
||||||
float Rotate)
|
|
||||||
{
|
|
||||||
Matrix2 Transform;
|
|
||||||
|
|
||||||
Transform = Matrix2.CreateScale(ScaleX, ScaleY);
|
|
||||||
Transform *= Matrix2.CreateRotation(Rotate);
|
|
||||||
|
|
||||||
Vector2 Offs = new Vector2(OffsX, OffsY);
|
|
||||||
|
|
||||||
FbRenderer.Set(Fb, Width, Height, Transform, Offs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBlendEnable(bool Enable)
|
public void SetBlendEnable(bool Enable)
|
||||||
|
@ -132,19 +99,46 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFb(int FbIndex, int Width, int Height)
|
public void CreateFrameBuffer(long Tag, int Width, int Height)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height));
|
ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BindFrameBuffer(int FbIndex)
|
public void BindFrameBuffer(long Tag)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex));
|
ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawFrameBuffer(int FbIndex)
|
public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex));
|
ActionsQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
FrameBuffer.BindTexture(Tag, Index);
|
||||||
|
|
||||||
|
OGLTexture.Set(Sampler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFrameBuffer(long Tag)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFrameBuffer(byte[] Data, int Width, int Height)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY)
|
||||||
|
{
|
||||||
|
Matrix2 Transform;
|
||||||
|
|
||||||
|
Transform = Matrix2.CreateScale(SX, SY);
|
||||||
|
Transform *= Matrix2.CreateRotation(Rotate);
|
||||||
|
|
||||||
|
Vector2 Offs = new Vector2(TX, TY);
|
||||||
|
|
||||||
|
ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
|
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
|
||||||
|
@ -239,14 +233,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue.Enqueue(() => Shader.BindProgram());
|
ActionsQueue.Enqueue(() => Shader.BindProgram());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTexture(int Index, GalTexture Tex)
|
public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => Texture.Set(Index, Tex));
|
ActionsQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
this.Texture.Set(Index, Texture);
|
||||||
|
|
||||||
|
OGLTexture.Set(Sampler);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSampler(int Index, GalTextureSampler Sampler)
|
public void BindTexture(int Index)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler));
|
ActionsQueue.Enqueue(() => Texture.Bind(Index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
|
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
|
||||||
{
|
{
|
||||||
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType);
|
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
|
||||||
|
|
||||||
ShaderIrNode[] Nodes = Block.GetNodes();
|
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||||
|
|
||||||
|
|
|
@ -74,11 +74,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
for (int Ch = 0; Ch < 4; Ch++)
|
for (int Ch = 0; Ch < 4; Ch++)
|
||||||
{
|
{
|
||||||
ShaderIrOperGpr Dst = (Ch >> 1) != 0
|
//Assign it to a temp because the destination registers
|
||||||
? GetOperGpr28(OpCode)
|
//may be used as texture coord input aswell.
|
||||||
: GetOperGpr0 (OpCode);
|
ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
|
||||||
|
|
||||||
Dst.Index += Ch & 1;
|
|
||||||
|
|
||||||
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
|
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
|
||||||
|
|
||||||
|
@ -86,6 +84,19 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int Ch = 0; Ch < 4; Ch++)
|
||||||
|
{
|
||||||
|
ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
|
||||||
|
|
||||||
|
ShaderIrOperGpr Dst = (Ch >> 1) != 0
|
||||||
|
? GetOperGpr28(OpCode)
|
||||||
|
: GetOperGpr0 (OpCode);
|
||||||
|
|
||||||
|
Dst.Index += Ch & 1;
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
static class ShaderDecoder
|
static class ShaderDecoder
|
||||||
{
|
{
|
||||||
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType)
|
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
|
||||||
{
|
{
|
||||||
ShaderIrBlock Block = new ShaderIrBlock();
|
ShaderIrBlock Block = new ShaderIrBlock();
|
||||||
|
|
||||||
|
@ -37,8 +37,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Block.RunOptimizationPasses(ShaderType);
|
|
||||||
|
|
||||||
return Block;
|
return Block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Nodes.Add(Node);
|
Nodes.Add(Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunOptimizationPasses(GalShaderType ShaderType)
|
|
||||||
{
|
|
||||||
ShaderOptExprProp.Optimize(Nodes, ShaderType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderIrNode[] GetNodes()
|
public ShaderIrNode[] GetNodes()
|
||||||
{
|
{
|
||||||
return Nodes.ToArray();
|
return Nodes.ToArray();
|
||||||
|
|
|
@ -1,366 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.Shader
|
|
||||||
{
|
|
||||||
static class ShaderOptExprProp
|
|
||||||
{
|
|
||||||
private struct UseSite
|
|
||||||
{
|
|
||||||
public ShaderIrNode Parent { get; private set; }
|
|
||||||
public ShaderIrCond Cond { get; private set; }
|
|
||||||
|
|
||||||
public int UseIndex { get; private set; }
|
|
||||||
|
|
||||||
public int OperIndex { get; private set; }
|
|
||||||
|
|
||||||
public UseSite(
|
|
||||||
ShaderIrNode Parent,
|
|
||||||
ShaderIrCond Cond,
|
|
||||||
int UseIndex,
|
|
||||||
int OperIndex)
|
|
||||||
{
|
|
||||||
this.Parent = Parent;
|
|
||||||
this.Cond = Cond;
|
|
||||||
this.UseIndex = UseIndex;
|
|
||||||
this.OperIndex = OperIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RegUse
|
|
||||||
{
|
|
||||||
public ShaderIrAsg Asg { get; private set; }
|
|
||||||
|
|
||||||
public int AsgIndex { get; private set; }
|
|
||||||
|
|
||||||
public int LastSiteIndex { get; private set; }
|
|
||||||
|
|
||||||
public ShaderIrCond Cond { get; private set; }
|
|
||||||
|
|
||||||
private List<UseSite> Sites;
|
|
||||||
|
|
||||||
public RegUse()
|
|
||||||
{
|
|
||||||
Sites = new List<UseSite>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddUseSite(UseSite Site)
|
|
||||||
{
|
|
||||||
if (LastSiteIndex < Site.UseIndex)
|
|
||||||
{
|
|
||||||
LastSiteIndex = Site.UseIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sites.Add(Site);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryPropagate()
|
|
||||||
{
|
|
||||||
//This happens when a untiliazied register is used,
|
|
||||||
//this usually indicates a decoding error, but may also
|
|
||||||
//be caused by bogus programs (?). In any case, we just
|
|
||||||
//keep the unitialized access and avoid trying to propagate
|
|
||||||
//the expression (since we can't propagate what doesn't yet exist).
|
|
||||||
if (Asg == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Cond != null)
|
|
||||||
{
|
|
||||||
//If the assignment is conditional, we can only propagate
|
|
||||||
//to the use sites that shares the same condition of the assignment.
|
|
||||||
foreach (UseSite Site in Sites)
|
|
||||||
{
|
|
||||||
if (!IsSameCondition(Cond, Site.Cond))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Sites.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (UseSite Site in Sites)
|
|
||||||
{
|
|
||||||
if (Site.Parent is ShaderIrCond Cond)
|
|
||||||
{
|
|
||||||
switch (Site.OperIndex)
|
|
||||||
{
|
|
||||||
case 0: Cond.Pred = Asg.Src; break;
|
|
||||||
case 1: Cond.Child = Asg.Src; break;
|
|
||||||
|
|
||||||
default: throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Site.Parent is ShaderIrOp Op)
|
|
||||||
{
|
|
||||||
switch (Site.OperIndex)
|
|
||||||
{
|
|
||||||
case 0: Op.OperandA = Asg.Src; break;
|
|
||||||
case 1: Op.OperandB = Asg.Src; break;
|
|
||||||
case 2: Op.OperandC = Asg.Src; break;
|
|
||||||
|
|
||||||
default: throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Site.Parent is ShaderIrAsg SiteAsg)
|
|
||||||
{
|
|
||||||
SiteAsg.Src = Asg.Src;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
|
|
||||||
{
|
|
||||||
this.Asg = Asg;
|
|
||||||
this.AsgIndex = AsgIndex;
|
|
||||||
this.Cond = Cond;
|
|
||||||
|
|
||||||
LastSiteIndex = 0;
|
|
||||||
|
|
||||||
Sites.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
|
|
||||||
{
|
|
||||||
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
|
|
||||||
|
|
||||||
RegUse GetUse(int Key)
|
|
||||||
{
|
|
||||||
RegUse Use;
|
|
||||||
|
|
||||||
if (!Uses.TryGetValue(Key, out Use))
|
|
||||||
{
|
|
||||||
Use = new RegUse();
|
|
||||||
|
|
||||||
Uses.Add(Key, Use);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Use;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetGprKey(int GprIndex)
|
|
||||||
{
|
|
||||||
return GprIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetPredKey(int PredIndex)
|
|
||||||
{
|
|
||||||
return PredIndex | 0x10000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegUse GetGprUse(int GprIndex)
|
|
||||||
{
|
|
||||||
return GetUse(GetGprKey(GprIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
RegUse GetPredUse(int PredIndex)
|
|
||||||
{
|
|
||||||
return GetUse(GetPredKey(PredIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveUse(RegUse Use)
|
|
||||||
{
|
|
||||||
if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FindRegUses(
|
|
||||||
List<(int, UseSite)> UseList,
|
|
||||||
ShaderIrNode Parent,
|
|
||||||
ShaderIrNode Node,
|
|
||||||
ShaderIrCond CondNode,
|
|
||||||
int UseIndex,
|
|
||||||
int OperIndex = 0)
|
|
||||||
{
|
|
||||||
if (Node is ShaderIrAsg Asg)
|
|
||||||
{
|
|
||||||
FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
|
|
||||||
}
|
|
||||||
else if (Node is ShaderIrCond Cond)
|
|
||||||
{
|
|
||||||
FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0);
|
|
||||||
FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
|
|
||||||
}
|
|
||||||
else if (Node is ShaderIrOp Op)
|
|
||||||
{
|
|
||||||
FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
|
|
||||||
FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
|
|
||||||
FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
|
|
||||||
}
|
|
||||||
else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
|
|
||||||
{
|
|
||||||
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
|
|
||||||
}
|
|
||||||
else if (Node is ShaderIrOperPred Pred)
|
|
||||||
{
|
|
||||||
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
|
|
||||||
{
|
|
||||||
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
|
|
||||||
|
|
||||||
FindRegUses(UseList, null, Node, CondNode, UseIndex);
|
|
||||||
|
|
||||||
foreach ((int Key, UseSite Site) in UseList)
|
|
||||||
{
|
|
||||||
GetUse(Key).AddUseSite(Site);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryPropagate(RegUse Use)
|
|
||||||
{
|
|
||||||
//We can only propagate if the registers that the expression depends
|
|
||||||
//on weren't assigned after the original expression assignment
|
|
||||||
//to a register took place. We traverse the expression tree to find
|
|
||||||
//all registers being used, if any of those registers was assigned
|
|
||||||
//after the assignment to be propagated, then we can't propagate.
|
|
||||||
if (Use?.Asg == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
|
|
||||||
|
|
||||||
if (Use.Cond != null)
|
|
||||||
{
|
|
||||||
FindRegUses(UseList, null, Use.Cond, null, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ((int Key, UseSite Site) in UseList)
|
|
||||||
{
|
|
||||||
//TODO: Build an assignment list inside RegUse,
|
|
||||||
//and check if there is an assignment inside the
|
|
||||||
//range of Use.AsgIndex and Use.LastSiteIndex,
|
|
||||||
//and if that's the case, then we should return false.
|
|
||||||
//The current method is too conservative.
|
|
||||||
if (GetUse(Key).AsgIndex >= Use.AsgIndex)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Use.TryPropagate();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
|
|
||||||
{
|
|
||||||
ShaderIrNode Node = Nodes[Index];
|
|
||||||
|
|
||||||
ShaderIrCond CondNode = null;
|
|
||||||
|
|
||||||
if (Node is ShaderIrCond)
|
|
||||||
{
|
|
||||||
CondNode = (ShaderIrCond)Node;
|
|
||||||
}
|
|
||||||
|
|
||||||
TryAddRegUseSite(Node, CondNode, IterCount);;
|
|
||||||
|
|
||||||
while (Node is ShaderIrCond Cond)
|
|
||||||
{
|
|
||||||
Node = Cond.Child;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(Node is ShaderIrAsg Asg))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegUse Use = null;
|
|
||||||
|
|
||||||
if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
|
|
||||||
{
|
|
||||||
Use = GetGprUse(Gpr.Index);
|
|
||||||
}
|
|
||||||
else if (Asg.Dst is ShaderIrOperPred Pred)
|
|
||||||
{
|
|
||||||
Use = GetPredUse(Pred.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanRemoveAsg = CondNode == null;
|
|
||||||
|
|
||||||
CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
|
|
||||||
|
|
||||||
if (CanRemoveAsg && TryPropagate(Use))
|
|
||||||
{
|
|
||||||
RemoveUse(Use);
|
|
||||||
|
|
||||||
//Note: Only decrement if the removal was successful.
|
|
||||||
//RemoveUse throws when this is not the case so we should be good.
|
|
||||||
Index--;
|
|
||||||
}
|
|
||||||
|
|
||||||
//All nodes inside conditional nodes can't be propagated,
|
|
||||||
//as we don't even know if they will be executed to begin with.
|
|
||||||
Use?.SetNewAsg(Asg, IterCount, CondNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (RegUse Use in Uses.Values)
|
|
||||||
{
|
|
||||||
//Gprs 0-3 are the color output on fragment shaders,
|
|
||||||
//so we can't remove the last assignments to those registers.
|
|
||||||
if (ShaderType == GalShaderType.Fragment)
|
|
||||||
{
|
|
||||||
if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryPropagate(Use))
|
|
||||||
{
|
|
||||||
RemoveUse(Use);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
|
|
||||||
{
|
|
||||||
if (CondA == null || CondB == null)
|
|
||||||
{
|
|
||||||
return CondA == CondB;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CondA.Not != CondB.Not)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CondA.Pred is ShaderIrOperPred PredA)
|
|
||||||
{
|
|
||||||
if (!(CondB.Pred is ShaderIrOperPred PredB))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PredA.Index != PredB.Index)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (CondA.Pred != CondB.Pred)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
internal NsGpuMemoryMgr MemoryMgr { get; private set; }
|
internal NsGpuMemoryMgr MemoryMgr { get; private set; }
|
||||||
|
|
||||||
public NvGpuFifo Fifo;
|
public NvGpuFifo Fifo { get; private set; }
|
||||||
|
|
||||||
internal NvGpuEngine3d Engine3d;
|
public NvGpuEngine3d Engine3d { get; private set; }
|
||||||
|
|
||||||
private Thread FifoProcessing;
|
private Thread FifoProcessing;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
KeepRunning = true;
|
KeepRunning = true;
|
||||||
|
|
||||||
FifoProcessing = new Thread(ProcessFifo);
|
FifoProcessing = new Thread(ProcessFifo);
|
||||||
|
|
||||||
FifoProcessing.Start();
|
FifoProcessing.Start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
class NvGpuEngine3d : INvGpuEngine
|
public class NvGpuEngine3d : INvGpuEngine
|
||||||
{
|
{
|
||||||
public int[] Registers { get; private set; }
|
public int[] Registers { get; private set; }
|
||||||
|
|
||||||
|
@ -20,9 +20,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
public int Size;
|
public int Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstBuffer[] Cbs;
|
private ConstBuffer[] ConstBuffers;
|
||||||
|
|
||||||
private bool HasDataToRender;
|
private HashSet<long> FrameBuffers;
|
||||||
|
|
||||||
public NvGpuEngine3d(NsGpu Gpu)
|
public NvGpuEngine3d(NsGpu Gpu)
|
||||||
{
|
{
|
||||||
|
@ -48,7 +48,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
AddMethod(0x8e4, 16, 1, CbData);
|
AddMethod(0x8e4, 16, 1, CbData);
|
||||||
AddMethod(0x904, 1, 1, CbBind);
|
AddMethod(0x904, 1, 1, CbBind);
|
||||||
|
|
||||||
Cbs = new ConstBuffer[18];
|
ConstBuffers = new ConstBuffer[18];
|
||||||
|
|
||||||
|
FrameBuffers = new HashSet<long>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
|
@ -76,19 +78,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
UploadTextures(Memory, Tags);
|
UploadTextures(Memory, Tags);
|
||||||
UploadUniforms(Memory);
|
UploadUniforms(Memory);
|
||||||
UploadVertexArrays(Memory);
|
UploadVertexArrays(Memory);
|
||||||
|
|
||||||
HasDataToRender = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
|
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
|
||||||
{
|
{
|
||||||
if (HasDataToRender)
|
|
||||||
{
|
|
||||||
HasDataToRender = false;
|
|
||||||
|
|
||||||
Gpu.Renderer.DrawFrameBuffer(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Arg0 = PBEntry.Arguments[0];
|
int Arg0 = PBEntry.Arguments[0];
|
||||||
|
|
||||||
int FbIndex = (Arg0 >> 6) & 0xf;
|
int FbIndex = (Arg0 >> 6) & 0xf;
|
||||||
|
@ -99,16 +92,23 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
SetFrameBuffer(0);
|
SetFrameBuffer(0);
|
||||||
|
|
||||||
Gpu.Renderer.ClearBuffers(Layer, Flags);
|
//TODO: Enable this once the frame buffer problems are fixed.
|
||||||
|
//Gpu.Renderer.ClearBuffers(Layer, Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetFrameBuffer(int FbIndex)
|
private void SetFrameBuffer(int FbIndex)
|
||||||
{
|
{
|
||||||
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
|
long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
|
||||||
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
|
|
||||||
|
|
||||||
Gpu.Renderer.SetFb(FbIndex, Width, Height);
|
FrameBuffers.Add(Address);
|
||||||
Gpu.Renderer.BindFrameBuffer(FbIndex);
|
|
||||||
|
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
|
||||||
|
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
|
||||||
|
|
||||||
|
//Note: Using the Width/Height results seems to give incorrect results.
|
||||||
|
//Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
|
||||||
|
Gpu.Renderer.CreateFrameBuffer(Address, 1280, 720);
|
||||||
|
Gpu.Renderer.BindFrameBuffer(Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long[] UploadShaders(AMemory Memory)
|
private long[] UploadShaders(AMemory Memory)
|
||||||
|
@ -167,23 +167,24 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
private void SetAlphaBlending()
|
private void SetAlphaBlending()
|
||||||
{
|
{
|
||||||
bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0;
|
//TODO: Support independent blend properly.
|
||||||
|
bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendEnable) & 1) != 0;
|
||||||
|
|
||||||
Gpu.Renderer.SetBlendEnable(BlendEnableMaster);
|
Gpu.Renderer.SetBlendEnable(Enable);
|
||||||
|
|
||||||
bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0;
|
bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0;
|
||||||
|
|
||||||
GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb);
|
GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb);
|
||||||
|
|
||||||
GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb);
|
GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb);
|
||||||
GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb);
|
GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb);
|
||||||
|
|
||||||
if (BlendSeparateAlpha)
|
if (BlendSeparateAlpha)
|
||||||
{
|
{
|
||||||
GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha);
|
GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha);
|
||||||
|
|
||||||
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha);
|
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
|
||||||
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha);
|
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
|
||||||
|
|
||||||
Gpu.Renderer.SetBlendSeparate(
|
Gpu.Renderer.SetBlendSeparate(
|
||||||
EquationRgb,
|
EquationRgb,
|
||||||
|
@ -205,11 +206,13 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
|
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
|
||||||
|
|
||||||
long BasePosition = Cbs[TextureCbIndex].Position;
|
long BasePosition = ConstBuffers[TextureCbIndex].Position;
|
||||||
|
|
||||||
long Size = (uint)Cbs[TextureCbIndex].Size;
|
long Size = (uint)ConstBuffers[TextureCbIndex].Size;
|
||||||
|
|
||||||
int TexIndex = 0;
|
//Note: On the emulator renderer, Texture Unit 0 is
|
||||||
|
//reserved for drawing the frame buffer.
|
||||||
|
int TexIndex = 1;
|
||||||
|
|
||||||
for (int Index = 0; Index < Tags.Length; Index++)
|
for (int Index = 0; Index < Tags.Length; Index++)
|
||||||
{
|
{
|
||||||
|
@ -241,8 +244,25 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
TicPosition += TicIndex * 0x20;
|
TicPosition += TicIndex * 0x20;
|
||||||
TscPosition += TscIndex * 0x20;
|
TscPosition += TscIndex * 0x20;
|
||||||
|
|
||||||
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
|
GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition);
|
||||||
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
|
|
||||||
|
long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff;
|
||||||
|
|
||||||
|
if (FrameBuffers.Contains(TextureAddress))
|
||||||
|
{
|
||||||
|
//This texture is a frame buffer texture,
|
||||||
|
//we shouldn't read anything from memory and bind
|
||||||
|
//the frame buffer texture instead, since we're not
|
||||||
|
//really writing anything to memory.
|
||||||
|
Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
|
||||||
|
|
||||||
|
Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
|
||||||
|
Gpu.Renderer.BindTexture(TexIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UploadUniforms(AMemory Memory)
|
private void UploadUniforms(AMemory Memory)
|
||||||
|
@ -262,9 +282,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++)
|
for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
|
||||||
{
|
{
|
||||||
ConstBuffer Cb = Cbs[Cbuf];
|
ConstBuffer Cb = ConstBuffers[Cbuf];
|
||||||
|
|
||||||
if (Cb.Enabled)
|
if (Cb.Enabled)
|
||||||
{
|
{
|
||||||
|
@ -379,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
if (Mode == 0)
|
if (Mode == 0)
|
||||||
{
|
{
|
||||||
//Write.
|
//Write mode.
|
||||||
Memory.WriteInt32(Position, Seq);
|
Memory.WriteInt32(Position, Seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,16 +434,16 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
|
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
|
||||||
{
|
{
|
||||||
Cbs[Index].Position = Position;
|
ConstBuffers[Index].Position = Position;
|
||||||
Cbs[Index].Enabled = Enabled;
|
ConstBuffers[Index].Enabled = Enabled;
|
||||||
|
|
||||||
Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
|
ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ReadCb(AMemory Memory, int Cbuf, int Offset)
|
private int ReadCb(AMemory Memory, int Cbuf, int Offset)
|
||||||
{
|
{
|
||||||
long Position = Cbs[Cbuf].Position;
|
long Position = ConstBuffers[Cbuf].Position;
|
||||||
|
|
||||||
int Value = Memory.ReadInt32(Position + Offset);
|
int Value = Memory.ReadInt32(Position + Offset);
|
||||||
|
|
||||||
|
@ -465,5 +485,10 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
Registers[(int)Reg] = Value;
|
Registers[(int)Reg] = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsFrameBufferPosition(long Position)
|
||||||
|
{
|
||||||
|
return FrameBuffers.Contains(Position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
FrameBufferNHeight = 0x203,
|
FrameBufferNHeight = 0x203,
|
||||||
FrameBufferNFormat = 0x204,
|
FrameBufferNFormat = 0x204,
|
||||||
VertexAttribNFormat = 0x458,
|
VertexAttribNFormat = 0x458,
|
||||||
|
IBlendEnable = 0x4b9,
|
||||||
BlendSeparateAlpha = 0x4cf,
|
BlendSeparateAlpha = 0x4cf,
|
||||||
BlendEquationRgb = 0x4d0,
|
BlendEquationRgb = 0x4d0,
|
||||||
BlendFuncSrcRgb = 0x4d1,
|
BlendFuncSrcRgb = 0x4d1,
|
||||||
|
@ -31,6 +32,13 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
VertexArrayNControl = 0x700,
|
VertexArrayNControl = 0x700,
|
||||||
VertexArrayNAddress = 0x701,
|
VertexArrayNAddress = 0x701,
|
||||||
VertexArrayNDivisor = 0x703,
|
VertexArrayNDivisor = 0x703,
|
||||||
|
IBlendNSeparateAlpha = 0x780,
|
||||||
|
IBlendNEquationRgb = 0x781,
|
||||||
|
IBlendNFuncSrcRgb = 0x782,
|
||||||
|
IBlendNFuncDstRgb = 0x783,
|
||||||
|
IBlendNEquationAlpha = 0x784,
|
||||||
|
IBlendNFuncSrcAlpha = 0x785,
|
||||||
|
IBlendNFuncDstAlpha = 0x786,
|
||||||
VertexArrayNEndAddr = 0x7c0,
|
VertexArrayNEndAddr = 0x7c0,
|
||||||
ShaderNControl = 0x800,
|
ShaderNControl = 0x800,
|
||||||
ShaderNOffset = 0x801,
|
ShaderNOffset = 0x801,
|
||||||
|
|
|
@ -2,7 +2,7 @@ using Ryujinx.Graphics.Gal;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
struct Texture
|
public struct Texture
|
||||||
{
|
{
|
||||||
public long Position { get; private set; }
|
public long Position { get; private set; }
|
||||||
|
|
||||||
|
@ -16,6 +16,24 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
|
|
||||||
public GalTextureFormat Format { get; private set; }
|
public GalTextureFormat Format { get; private set; }
|
||||||
|
|
||||||
|
public Texture(
|
||||||
|
long Position,
|
||||||
|
int Width,
|
||||||
|
int Height)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
this.Width = Width;
|
||||||
|
this.Height = Height;
|
||||||
|
|
||||||
|
Pitch = 0;
|
||||||
|
|
||||||
|
BlockHeight = 16;
|
||||||
|
|
||||||
|
Swizzle = TextureSwizzle.BlockLinear;
|
||||||
|
|
||||||
|
Format = GalTextureFormat.A8B8G8R8;
|
||||||
|
}
|
||||||
|
|
||||||
public Texture(
|
public Texture(
|
||||||
long Position,
|
long Position,
|
||||||
int Width,
|
int Width,
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
static class TextureReader
|
public static class TextureReader
|
||||||
{
|
{
|
||||||
public static byte[] Read(AMemory Memory, Texture Texture)
|
public static byte[] Read(AMemory Memory, Texture Texture)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
enum TextureSwizzle
|
public enum TextureSwizzle
|
||||||
{
|
{
|
||||||
_1dBuffer = 0,
|
_1dBuffer = 0,
|
||||||
PitchColorKey = 1,
|
PitchColorKey = 1,
|
||||||
|
|
|
@ -38,8 +38,6 @@ namespace Ryujinx
|
||||||
protected override void OnLoad(EventArgs e)
|
protected override void OnLoad(EventArgs e)
|
||||||
{
|
{
|
||||||
VSync = VSyncMode.On;
|
VSync = VSyncMode.On;
|
||||||
|
|
||||||
Renderer.InitializeFrameBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||||
|
|
Loading…
Reference in a new issue