Somewhat better NvFlinger (I guess) (fixes #30)
This commit is contained in:
parent
eafc58c9f2
commit
2ed733b1d5
14 changed files with 820 additions and 444 deletions
60
Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs
Normal file
60
Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Android
|
||||
{
|
||||
struct GbpBuffer
|
||||
{
|
||||
public int Magic { get; private set; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public int Stride { get; private set; }
|
||||
public int Format { get; private set; }
|
||||
public int Usage { get; private set; }
|
||||
|
||||
public int Pid { get; private set; }
|
||||
public int RefCount { get; private set; }
|
||||
|
||||
public int FdsCount { get; private set; }
|
||||
public int IntsCount { get; private set; }
|
||||
|
||||
public byte[] RawData { get; private set; }
|
||||
|
||||
public int Size => RawData.Length + 10 * 4;
|
||||
|
||||
public GbpBuffer(BinaryReader Reader)
|
||||
{
|
||||
Magic = Reader.ReadInt32();
|
||||
Width = Reader.ReadInt32();
|
||||
Height = Reader.ReadInt32();
|
||||
Stride = Reader.ReadInt32();
|
||||
Format = Reader.ReadInt32();
|
||||
Usage = Reader.ReadInt32();
|
||||
|
||||
Pid = Reader.ReadInt32();
|
||||
RefCount = Reader.ReadInt32();
|
||||
|
||||
FdsCount = Reader.ReadInt32();
|
||||
IntsCount = Reader.ReadInt32();
|
||||
|
||||
RawData = Reader.ReadBytes((FdsCount + IntsCount) * 4);
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter Writer)
|
||||
{
|
||||
Writer.Write(Magic);
|
||||
Writer.Write(Width);
|
||||
Writer.Write(Height);
|
||||
Writer.Write(Stride);
|
||||
Writer.Write(Format);
|
||||
Writer.Write(Usage);
|
||||
|
||||
Writer.Write(Pid);
|
||||
Writer.Write(RefCount);
|
||||
|
||||
Writer.Write(FdsCount);
|
||||
Writer.Write(IntsCount);
|
||||
|
||||
Writer.Write(RawData);
|
||||
}
|
||||
}
|
||||
}
|
392
Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
Normal file
392
Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
Normal file
|
@ -0,0 +1,392 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Android
|
||||
{
|
||||
class NvFlinger : IDisposable
|
||||
{
|
||||
private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader);
|
||||
|
||||
private Dictionary<(string, int), ServiceProcessParcel> Commands;
|
||||
|
||||
private const int BufferQueueCount = 0x40;
|
||||
private const int BufferQueueMask = BufferQueueCount - 1;
|
||||
|
||||
[Flags]
|
||||
private enum HalTransform
|
||||
{
|
||||
FlipX = 1 << 0,
|
||||
FlipY = 1 << 1,
|
||||
Rotate90 = 1 << 2
|
||||
}
|
||||
|
||||
private enum BufferState
|
||||
{
|
||||
Free,
|
||||
Dequeued,
|
||||
Queued,
|
||||
Acquired
|
||||
}
|
||||
|
||||
private struct BufferEntry
|
||||
{
|
||||
public BufferState State;
|
||||
|
||||
public HalTransform Transform;
|
||||
|
||||
public GbpBuffer Data;
|
||||
}
|
||||
|
||||
private BufferEntry[] BufferQueue;
|
||||
|
||||
private ManualResetEvent WaitBufferFree;
|
||||
|
||||
private bool KeepRunning;
|
||||
|
||||
public NvFlinger()
|
||||
{
|
||||
Commands = new Dictionary<(string, int), ServiceProcessParcel>()
|
||||
{
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
|
||||
};
|
||||
|
||||
BufferQueue = new BufferEntry[0x40];
|
||||
|
||||
WaitBufferFree = new ManualResetEvent(false);
|
||||
|
||||
KeepRunning = true;
|
||||
}
|
||||
|
||||
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream(ParcelData))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(4, SeekOrigin.Current);
|
||||
|
||||
int StrSize = Reader.ReadInt32();
|
||||
|
||||
string InterfaceName = Encoding.Unicode.GetString(Reader.ReadBytes(StrSize * 2));
|
||||
|
||||
long Remainder = MS.Position & 0xf;
|
||||
|
||||
if (Remainder != 0)
|
||||
{
|
||||
MS.Seek(0x10 - Remainder, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
MS.Seek(0x50, SeekOrigin.Begin);
|
||||
|
||||
if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
|
||||
{
|
||||
Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
|
||||
|
||||
return ProcReq(Context, Reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{InterfaceName} {Code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long GbpRequestBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
int Slot = ParcelReader.ReadInt32();
|
||||
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
BufferEntry Entry = BufferQueue[Slot];
|
||||
|
||||
int BufferCount = 1; //?
|
||||
long BufferSize = Entry.Data.Size;
|
||||
|
||||
Writer.Write(BufferCount);
|
||||
Writer.Write(BufferSize);
|
||||
|
||||
Entry.Data.Write(Writer);
|
||||
|
||||
Writer.Write(0);
|
||||
|
||||
return MakeReplyParcel(Context, MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private long GbpDequeueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
//TODO: Errors.
|
||||
int Format = ParcelReader.ReadInt32();
|
||||
int Width = ParcelReader.ReadInt32();
|
||||
int Height = ParcelReader.ReadInt32();
|
||||
int GetTimestamps = ParcelReader.ReadInt32();
|
||||
int Usage = ParcelReader.ReadInt32();
|
||||
|
||||
int Slot = GetFreeSlotBlocking(Width, Height);
|
||||
|
||||
return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
//TODO: Errors.
|
||||
int Slot = ParcelReader.ReadInt32();
|
||||
int Unknown4 = ParcelReader.ReadInt32();
|
||||
int Unknown8 = ParcelReader.ReadInt32();
|
||||
int Unknownc = ParcelReader.ReadInt32();
|
||||
int Timestamp = ParcelReader.ReadInt32();
|
||||
int IsAutoTimestamp = ParcelReader.ReadInt32();
|
||||
int CropTop = ParcelReader.ReadInt32();
|
||||
int CropLeft = ParcelReader.ReadInt32();
|
||||
int CropRight = ParcelReader.ReadInt32();
|
||||
int CropBottom = ParcelReader.ReadInt32();
|
||||
int ScalingMode = ParcelReader.ReadInt32();
|
||||
int Transform = ParcelReader.ReadInt32();
|
||||
int StickyTransform = ParcelReader.ReadInt32();
|
||||
int Unknown34 = ParcelReader.ReadInt32();
|
||||
int Unknown38 = ParcelReader.ReadInt32();
|
||||
int IsFenceValid = ParcelReader.ReadInt32();
|
||||
int Fence0Id = ParcelReader.ReadInt32();
|
||||
int Fence0Value = ParcelReader.ReadInt32();
|
||||
int Fence1Id = ParcelReader.ReadInt32();
|
||||
int Fence1Value = ParcelReader.ReadInt32();
|
||||
|
||||
BufferQueue[Slot].Transform = (HalTransform)Transform;
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Queued;
|
||||
|
||||
SendFrameBuffer(Context, Slot);
|
||||
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
//TODO: Errors.
|
||||
int Slot = ParcelReader.ReadInt32();
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Free;
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
|
||||
private long GbpQuery(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
return MakeReplyParcel(Context, 0, 0);
|
||||
}
|
||||
|
||||
private long GbpConnect(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||
{
|
||||
int Slot = ParcelReader.ReadInt32();
|
||||
|
||||
int BufferCount = ParcelReader.ReadInt32();
|
||||
long BufferSize = ParcelReader.ReadInt64();
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Free;
|
||||
|
||||
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
foreach (int Int in Ints)
|
||||
{
|
||||
Writer.Write(Int);
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
|
||||
{
|
||||
long ReplyPos = Context.Request.ReceiveBuff[0].Position;
|
||||
long ReplySize = Context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
byte[] Reply = MakeParcel(Data, new byte[0]);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
|
||||
{
|
||||
int FbWidth = BufferQueue[Slot].Data.Width;
|
||||
int FbHeight = BufferQueue[Slot].Data.Height;
|
||||
|
||||
int FbSize = FbWidth * FbHeight * 4;
|
||||
|
||||
HNvMap NvMap = GetNvMap(Context, Slot);
|
||||
|
||||
if (NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize)
|
||||
{
|
||||
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Free;
|
||||
|
||||
WaitBufferFree.Set();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Acquired;
|
||||
|
||||
float ScaleX = 1;
|
||||
float ScaleY = 1;
|
||||
float Rotate = 0;
|
||||
|
||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX))
|
||||
{
|
||||
ScaleX = -1;
|
||||
}
|
||||
|
||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY))
|
||||
{
|
||||
ScaleY = -1;
|
||||
}
|
||||
|
||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
|
||||
{
|
||||
Rotate = MathF.PI * 0.5f;
|
||||
}
|
||||
|
||||
byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address;
|
||||
|
||||
Context.Ns.Gpu.Renderer.QueueAction(delegate()
|
||||
{
|
||||
Context.Ns.Gpu.Renderer.SetFrameBuffer(
|
||||
Fb,
|
||||
FbWidth,
|
||||
FbHeight,
|
||||
ScaleX,
|
||||
ScaleY,
|
||||
Rotate);
|
||||
|
||||
BufferQueue[Slot].State = BufferState.Free;
|
||||
|
||||
WaitBufferFree.Set();
|
||||
});
|
||||
}
|
||||
|
||||
private HNvMap GetNvMap(ServiceCtx Context, int Slot)
|
||||
{
|
||||
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
byte[] RawValue = BitConverter.GetBytes(NvMapHandle);
|
||||
|
||||
Array.Reverse(RawValue);
|
||||
|
||||
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
|
||||
}
|
||||
|
||||
return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle);
|
||||
}
|
||||
|
||||
private int GetFreeSlotBlocking(int Width, int Height)
|
||||
{
|
||||
int Slot;
|
||||
|
||||
do
|
||||
{
|
||||
if ((Slot = GetFreeSlot(Width, Height)) != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Logging.Debug("Waiting for a free BufferQueue slot...");
|
||||
|
||||
lock (WaitBufferFree)
|
||||
{
|
||||
if (!KeepRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
WaitBufferFree.Reset();
|
||||
}
|
||||
|
||||
WaitBufferFree.WaitOne();
|
||||
}
|
||||
while (KeepRunning);
|
||||
|
||||
Logging.Debug($"Found free BufferQueue slot {Slot}!");
|
||||
|
||||
return Slot;
|
||||
}
|
||||
|
||||
private int GetFreeSlot(int Width, int Height)
|
||||
{
|
||||
lock (BufferQueue)
|
||||
{
|
||||
for (int Slot = 0; Slot < BufferQueue.Length; Slot++)
|
||||
{
|
||||
if (BufferQueue[Slot].State != BufferState.Free)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GbpBuffer Data = BufferQueue[Slot].Data;
|
||||
|
||||
if (Data.Width == Width &&
|
||||
Data.Height == Height)
|
||||
{
|
||||
BufferQueue[Slot].State = BufferState.Dequeued;
|
||||
|
||||
return Slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
lock (WaitBufferFree)
|
||||
{
|
||||
KeepRunning = false;
|
||||
|
||||
WaitBufferFree.Set();
|
||||
}
|
||||
|
||||
WaitBufferFree.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects
|
||||
{
|
||||
static class ErrorCode
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
|
|
@ -1,34 +1,18 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using Ryujinx.Core.OsHle.Utilities;
|
||||
using Ryujinx.Core.OsHle.Objects.Android;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||
{
|
||||
class IHOSBinderDriver : IIpcInterface
|
||||
class IHOSBinderDriver : IIpcInterface, IDisposable
|
||||
{
|
||||
private delegate long ServiceProcessParcel(ServiceCtx Context, byte[] ParcelData);
|
||||
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
private Dictionary<(string, int), ServiceProcessParcel> m_Methods;
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
private class BufferObj
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private IdPoolWithObj BufferSlots;
|
||||
|
||||
private byte[] Gbfr;
|
||||
private NvFlinger Flinger;
|
||||
|
||||
public IHOSBinderDriver()
|
||||
{
|
||||
|
@ -39,18 +23,7 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
|||
{ 2, GetNativeHandle }
|
||||
};
|
||||
|
||||
m_Methods = new Dictionary<(string, int), ServiceProcessParcel>()
|
||||
{
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect },
|
||||
{ ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer }
|
||||
};
|
||||
|
||||
BufferSlots = new IdPoolWithObj();
|
||||
Flinger = new NvFlinger();
|
||||
}
|
||||
|
||||
public long TransactParcel(ServiceCtx Context)
|
||||
|
@ -63,133 +36,9 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
|||
|
||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
|
||||
|
||||
Data = GetParcelData(Data);
|
||||
Data = Parcel.GetParcelData(Data);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(Data))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(4, SeekOrigin.Current);
|
||||
|
||||
int StrSize = Reader.ReadInt32();
|
||||
|
||||
string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2);
|
||||
|
||||
if (m_Methods.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
|
||||
{
|
||||
return ProcReq(Context, Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{InterfaceName} {Code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
int GbfrSize = Gbfr?.Length ?? 0;
|
||||
|
||||
byte[] Data = new byte[GbfrSize + 4];
|
||||
|
||||
if (Gbfr != null)
|
||||
{
|
||||
Buffer.BlockCopy(Gbfr, 0, Data, 0, GbfrSize);
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, Data);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
//Note: It seems that the maximum number of slots is 64, because if we return
|
||||
//a Slot number > 63, it seems to cause a buffer overrun and it reads garbage.
|
||||
//Note 2: The size of each object associated with the slot is 0x30.
|
||||
int Slot = BufferSlots.GenerateId(new BufferObj());
|
||||
|
||||
return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream(ParcelData))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(0x50, SeekOrigin.Begin);
|
||||
|
||||
int Slot = Reader.ReadInt32();
|
||||
|
||||
BufferSlots.Delete(Slot);
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData)
|
||||
{
|
||||
int GbfrSize = ParcelData.Length - 0x54;
|
||||
|
||||
Gbfr = new byte[GbfrSize];
|
||||
|
||||
Buffer.BlockCopy(ParcelData, 0x54, Gbfr, 0, GbfrSize);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(ParcelData))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
|
||||
MS.Seek(0xd4, SeekOrigin.Begin);
|
||||
|
||||
int Handle = Reader.ReadInt32();
|
||||
|
||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
||||
|
||||
Context.Ns.Gpu.Renderer.FrameBufferPtr = NvMap.Address;
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, 0);
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
BinaryWriter Writer = new BinaryWriter(MS);
|
||||
|
||||
foreach (int Int in Ints)
|
||||
{
|
||||
Writer.Write(Int);
|
||||
}
|
||||
|
||||
return MakeReplyParcel(Context, MS.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
|
||||
{
|
||||
long ReplyPos = Context.Request.ReceiveBuff[0].Position;
|
||||
long ReplySize = Context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
byte[] Reply = MakeParcel(Data, new byte[0]);
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
|
||||
|
||||
return 0;
|
||||
return Flinger.ProcessParcelRequest(Context, Data, Code);
|
||||
}
|
||||
|
||||
public long AdjustRefcount(ServiceCtx Context)
|
||||
|
@ -210,5 +59,18 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Flinger.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics/Gal/EmbeddedResource.cs
Normal file
20
Ryujinx.Graphics/Gal/EmbeddedResource.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
static class EmbeddedResource
|
||||
{
|
||||
public static string GetString(string Name)
|
||||
{
|
||||
Assembly Asm = typeof(EmbeddedResource).Assembly;
|
||||
|
||||
using (Stream ResStream = Asm.GetManifestResourceStream(Name))
|
||||
{
|
||||
StreamReader Reader = new StreamReader(ResStream);
|
||||
|
||||
return Reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,14 +2,15 @@ using System;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public interface IGalRenderer
|
||||
public unsafe interface IGalRenderer
|
||||
{
|
||||
long FrameBufferPtr { get; set; }
|
||||
|
||||
void QueueAction(Action ActionMthd);
|
||||
void RunActions();
|
||||
|
||||
void InitializeFrameBuffer();
|
||||
void Render();
|
||||
void SetWindowSize(int Width, int Height);
|
||||
void SetFrameBuffer(byte* Fb, int Width, int Height, float SX, float SY, float R);
|
||||
void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
|
||||
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
|
||||
void BindTexture(int Index);
|
||||
|
|
13
Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
Normal file
13
Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
Normal file
|
@ -0,0 +1,13 @@
|
|||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
|
||||
out vec4 out_frag_color;
|
||||
|
||||
void main(void) {
|
||||
out_frag_color = texture(tex, tex_coord);
|
||||
}
|
26
Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
Normal file
26
Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
Normal file
|
@ -0,0 +1,26 @@
|
|||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 window_size;
|
||||
uniform mat2 transform;
|
||||
|
||||
layout(location = 0) in vec2 in_position;
|
||||
layout(location = 1) in vec2 in_tex_coord;
|
||||
|
||||
out vec2 tex_coord;
|
||||
|
||||
// Have a fixed aspect ratio, fit the image within the available space.
|
||||
vec2 get_scale_ratio(void) {
|
||||
vec2 native_size = vec2(1280, 720);
|
||||
vec2 ratio = vec2(
|
||||
(window_size.y * native_size.x) / (native_size.y * window_size.x),
|
||||
(window_size.x * native_size.y) / (native_size.x * window_size.y)
|
||||
);
|
||||
return min(ratio, 1);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
tex_coord = in_tex_coord;
|
||||
gl_Position = vec4((transform * in_position) * get_scale_ratio(), 0, 1);
|
||||
}
|
228
Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
Normal file
228
Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
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;
|
||||
|
||||
public FrameBuffer(int Width, int Height)
|
||||
{
|
||||
if (Width < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Width));
|
||||
}
|
||||
|
||||
if (Height < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (Fb == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Fb));
|
||||
}
|
||||
|
||||
if (Width < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Width));
|
||||
}
|
||||
|
||||
if (Height < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
using OpenTK;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
public class OpenGLRenderer : IGalRenderer
|
||||
|
@ -25,6 +27,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
private Queue<Action> ActionsQueue;
|
||||
|
||||
private FrameBuffer FbRenderer;
|
||||
|
||||
public long FrameBufferPtr { get; set; }
|
||||
|
||||
public OpenGLRenderer()
|
||||
|
@ -36,6 +40,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
ActionsQueue = new Queue<Action>();
|
||||
}
|
||||
|
||||
public void InitializeFrameBuffer()
|
||||
{
|
||||
FbRenderer = new FrameBuffer(1280, 720);
|
||||
}
|
||||
|
||||
public void QueueAction(Action ActionMthd)
|
||||
{
|
||||
ActionsQueue.Enqueue(ActionMthd);
|
||||
|
@ -43,14 +52,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public void RunActions()
|
||||
{
|
||||
while (ActionsQueue.Count > 0)
|
||||
int Count = ActionsQueue.Count;
|
||||
|
||||
while (Count-- > 0)
|
||||
{
|
||||
ActionsQueue.Dequeue()();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
FbRenderer.Render();
|
||||
|
||||
for (int Index = 0; Index < VertexBuffers.Count; Index++)
|
||||
{
|
||||
VertexBuffer Vb = VertexBuffers[Index];
|
||||
|
@ -62,7 +75,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SetWindowSize(int Width, int Height)
|
||||
{
|
||||
FbRenderer.WindowWidth = Width;
|
||||
FbRenderer.WindowHeight = Height;
|
||||
}
|
||||
|
||||
public unsafe void SetFrameBuffer(
|
||||
byte* Fb,
|
||||
int Width,
|
||||
int Height,
|
||||
float ScaleX,
|
||||
float ScaleY,
|
||||
float Rotate)
|
||||
{
|
||||
Matrix2 Transform;
|
||||
|
||||
Transform = Matrix2.CreateScale(ScaleX, ScaleY);
|
||||
Transform *= Matrix2.CreateRotation(Rotate);
|
||||
|
||||
FbRenderer.Set(Fb, Width, Height, Transform);
|
||||
}
|
||||
|
||||
public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
||||
</ItemGroup>
|
||||
|
@ -12,4 +20,13 @@
|
|||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Gal\OpenGL\FbVtxShader.glsl">
|
||||
<LogicalName>GlFbVtxShader</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Gal\OpenGL\FbFragShader.glsl">
|
||||
<LogicalName>GlFbFragShader</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
// This code was written for the OpenTK library and has been released
|
||||
// to the Public Domain.
|
||||
// It is provided "as is" without express or implied warranty of any kind.
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
@ -13,281 +9,25 @@ namespace Ryujinx
|
|||
{
|
||||
public class GLScreen : GameWindow
|
||||
{
|
||||
class ScreenTexture : IDisposable
|
||||
{
|
||||
private Switch Ns;
|
||||
private IGalRenderer Renderer;
|
||||
|
||||
private int Width;
|
||||
private int Height;
|
||||
private int TexHandle;
|
||||
|
||||
private int[] Pixels;
|
||||
|
||||
public ScreenTexture(Switch Ns, IGalRenderer Renderer, int Width, int Height)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.Renderer = Renderer;
|
||||
this.Width = Width;
|
||||
this.Height = Height;
|
||||
|
||||
Pixels = new int[Width * Height];
|
||||
|
||||
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,
|
||||
Width,
|
||||
Height,
|
||||
0,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
public int Texture
|
||||
{
|
||||
get
|
||||
{
|
||||
UploadBitmap();
|
||||
|
||||
return TexHandle;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void UploadBitmap()
|
||||
{
|
||||
int FbSize = Width * Height * 4;
|
||||
|
||||
if (Renderer.FrameBufferPtr == 0 || Renderer.FrameBufferPtr + FbSize > uint.MaxValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte* SrcPtr = (byte*)Ns.Ram + (uint)Renderer.FrameBufferPtr;
|
||||
|
||||
for (int Y = 0; Y < Height; Y++)
|
||||
{
|
||||
for (int X = 0; X < Width; X++)
|
||||
{
|
||||
int SrcOffs = GetSwizzleOffset(X, Y, 4);
|
||||
|
||||
Pixels[X + Y * Width] = *((int*)(SrcPtr + SrcOffs));
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
||||
GL.TexSubImage2D(TextureTarget.Texture2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Width,
|
||||
Height,
|
||||
PixelFormat.Rgba,
|
||||
PixelType.UnsignedByte,
|
||||
Pixels);
|
||||
}
|
||||
|
||||
private int GetSwizzleOffset(int X, int Y, int Bpp)
|
||||
{
|
||||
int Pos;
|
||||
|
||||
Pos = (Y & 0x7f) >> 4;
|
||||
Pos += (X >> 4) << 3;
|
||||
Pos += (Y >> 7) * ((Width >> 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;
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
GL.DeleteTexture(TexHandle);
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string VtxShaderSource = @"
|
||||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 window_size;
|
||||
|
||||
layout(location = 0) in vec3 in_position;
|
||||
layout(location = 1) in vec4 in_color;
|
||||
layout(location = 2) in vec2 in_tex_coord;
|
||||
|
||||
out vec4 color;
|
||||
out vec2 tex_coord;
|
||||
|
||||
// Have a fixed aspect ratio, fit the image within the available space.
|
||||
vec3 get_scale_ratio() {
|
||||
vec2 native_size = vec2(1280, 720);
|
||||
vec2 ratio = vec2(
|
||||
(window_size.y * native_size.x) / (native_size.y * window_size.x),
|
||||
(window_size.x * native_size.y) / (native_size.x * window_size.y)
|
||||
);
|
||||
return vec3(min(ratio, vec2(1, 1)) * vec2(1, -1), 1);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
color = in_color;
|
||||
tex_coord = in_tex_coord;
|
||||
gl_Position = vec4(in_position * get_scale_ratio(), 1);
|
||||
}";
|
||||
|
||||
private string FragShaderSource = @"
|
||||
#version 330 core
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec4 color;
|
||||
in vec2 tex_coord;
|
||||
out vec4 out_frag_color;
|
||||
|
||||
void main(void) {
|
||||
out_frag_color = vec4(texture(tex, tex_coord).rgb, color.a);
|
||||
}";
|
||||
|
||||
private int VtxShaderHandle,
|
||||
FragShaderHandle,
|
||||
PrgShaderHandle;
|
||||
|
||||
private int WindowSizeUniformLocation;
|
||||
|
||||
private int VaoHandle;
|
||||
private int VboHandle;
|
||||
|
||||
private Switch Ns;
|
||||
|
||||
private IGalRenderer Renderer;
|
||||
|
||||
private ScreenTexture ScreenTex;
|
||||
|
||||
public GLScreen(Switch Ns, IGalRenderer Renderer)
|
||||
: base(1280, 720,
|
||||
new GraphicsMode(), "Ryujinx", 0,
|
||||
DisplayDevice.Default, 3, 3,
|
||||
GraphicsContextFlags.ForwardCompatible)
|
||||
{
|
||||
this.Ns = Ns;
|
||||
this.Ns = Ns;
|
||||
this.Renderer = Renderer;
|
||||
|
||||
ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720);
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
VSync = VSyncMode.On;
|
||||
|
||||
CreateShaders();
|
||||
CreateVbo();
|
||||
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
}
|
||||
|
||||
protected override void OnUnload(EventArgs e)
|
||||
{
|
||||
ScreenTex.Dispose();
|
||||
|
||||
GL.DeleteVertexArray(VaoHandle);
|
||||
GL.DeleteBuffer(VboHandle);
|
||||
}
|
||||
|
||||
private void CreateVbo()
|
||||
{
|
||||
VaoHandle = GL.GenVertexArray();
|
||||
VboHandle = GL.GenBuffer();
|
||||
|
||||
uint[] Buffer = new uint[]
|
||||
{
|
||||
0xbf800000, 0x3f800000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x3f800000, 0x3f800000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x00000000,
|
||||
0xbf800000, 0xbf800000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x3f800000,
|
||||
0x3f800000, 0xbf800000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x3f800000
|
||||
};
|
||||
|
||||
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, 3, VertexAttribPointerType.Float, false, 28, 0);
|
||||
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.UnsignedByte, false, 28, 12);
|
||||
|
||||
GL.EnableVertexAttribArray(2);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
||||
|
||||
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 28, 20);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
private void CreateShaders()
|
||||
{
|
||||
VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader);
|
||||
FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
|
||||
|
||||
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 TexLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
|
||||
GL.Uniform1(TexLocation, 0);
|
||||
|
||||
WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
|
||||
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
|
||||
Renderer.InitializeFrameBuffer();
|
||||
}
|
||||
|
||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||
|
@ -382,12 +122,7 @@ void main(void) {
|
|||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
RenderFb();
|
||||
|
||||
GL.UseProgram(PrgShaderHandle);
|
||||
|
||||
Renderer.RunActions();
|
||||
Renderer.BindTexture(0);
|
||||
Renderer.Render();
|
||||
|
||||
SwapBuffers();
|
||||
|
@ -395,16 +130,7 @@ void main(void) {
|
|||
|
||||
protected override void OnResize(EventArgs e)
|
||||
{
|
||||
GL.UseProgram(PrgShaderHandle);
|
||||
GL.Uniform2(WindowSizeUniformLocation, new Vector2(Width, Height));
|
||||
}
|
||||
|
||||
void RenderFb()
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, ScreenTex.Texture);
|
||||
GL.BindVertexArray(VaoHandle);
|
||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
||||
Renderer.SetWindowSize(Width, Height);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue