mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-23 07:15:28 +00:00
Somewhat better NvFlinger (I guess) (fixes #30)
This commit is contained in:
parent
eafc58c9f2
commit
2ed733b1d5
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
|
namespace Ryujinx.Core.OsHle.Objects
|
||||||
{
|
{
|
||||||
static class ErrorCode
|
static class ErrorCode
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,18 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using Ryujinx.Core.OsHle.Utilities;
|
using Ryujinx.Core.OsHle.Objects.Android;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
|
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Objects.Vi
|
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<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 NvFlinger Flinger;
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private IdPoolWithObj BufferSlots;
|
|
||||||
|
|
||||||
private byte[] Gbfr;
|
|
||||||
|
|
||||||
public IHOSBinderDriver()
|
public IHOSBinderDriver()
|
||||||
{
|
{
|
||||||
|
@ -39,18 +23,7 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||||
{ 2, GetNativeHandle }
|
{ 2, GetNativeHandle }
|
||||||
};
|
};
|
||||||
|
|
||||||
m_Methods = new Dictionary<(string, int), ServiceProcessParcel>()
|
Flinger = new NvFlinger();
|
||||||
{
|
|
||||||
{ ("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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long TransactParcel(ServiceCtx Context)
|
public long TransactParcel(ServiceCtx Context)
|
||||||
|
@ -63,133 +36,9 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||||
|
|
||||||
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
|
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
|
||||||
|
|
||||||
Data = GetParcelData(Data);
|
Data = Parcel.GetParcelData(Data);
|
||||||
|
|
||||||
using (MemoryStream MS = new MemoryStream(Data))
|
return Flinger.ProcessParcelRequest(Context, Data, Code);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long AdjustRefcount(ServiceCtx Context)
|
public long AdjustRefcount(ServiceCtx Context)
|
||||||
|
@ -210,5 +59,18 @@ namespace Ryujinx.Core.OsHle.Objects.Vi
|
||||||
|
|
||||||
return 0;
|
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
|
namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
public interface IGalRenderer
|
public unsafe interface IGalRenderer
|
||||||
{
|
{
|
||||||
long FrameBufferPtr { get; set; }
|
|
||||||
|
|
||||||
void QueueAction(Action ActionMthd);
|
void QueueAction(Action ActionMthd);
|
||||||
void RunActions();
|
void RunActions();
|
||||||
|
|
||||||
|
void InitializeFrameBuffer();
|
||||||
void Render();
|
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 SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
|
||||||
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
|
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
|
||||||
void BindTexture(int Index);
|
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 OpenTK.Graphics.OpenGL;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
public class OpenGLRenderer : IGalRenderer
|
public class OpenGLRenderer : IGalRenderer
|
||||||
|
@ -25,6 +27,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private Queue<Action> ActionsQueue;
|
private Queue<Action> ActionsQueue;
|
||||||
|
|
||||||
|
private FrameBuffer FbRenderer;
|
||||||
|
|
||||||
public long FrameBufferPtr { get; set; }
|
public long FrameBufferPtr { get; set; }
|
||||||
|
|
||||||
public OpenGLRenderer()
|
public OpenGLRenderer()
|
||||||
|
@ -36,6 +40,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue = new Queue<Action>();
|
ActionsQueue = new Queue<Action>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitializeFrameBuffer()
|
||||||
|
{
|
||||||
|
FbRenderer = new FrameBuffer(1280, 720);
|
||||||
|
}
|
||||||
|
|
||||||
public void QueueAction(Action ActionMthd)
|
public void QueueAction(Action ActionMthd)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(ActionMthd);
|
ActionsQueue.Enqueue(ActionMthd);
|
||||||
|
@ -43,7 +52,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public void RunActions()
|
public void RunActions()
|
||||||
{
|
{
|
||||||
while (ActionsQueue.Count > 0)
|
int Count = ActionsQueue.Count;
|
||||||
|
|
||||||
|
while (Count-- > 0)
|
||||||
{
|
{
|
||||||
ActionsQueue.Dequeue()();
|
ActionsQueue.Dequeue()();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +62,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
|
FbRenderer.Render();
|
||||||
|
|
||||||
for (int Index = 0; Index < VertexBuffers.Count; Index++)
|
for (int Index = 0; Index < VertexBuffers.Count; Index++)
|
||||||
{
|
{
|
||||||
VertexBuffer Vb = VertexBuffers[Index];
|
VertexBuffer Vb = VertexBuffers[Index];
|
||||||
|
@ -62,7 +75,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
|
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)
|
public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -12,4 +20,13 @@
|
||||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Gal\OpenGL\FbVtxShader.glsl">
|
||||||
|
<LogicalName>GlFbVtxShader</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Gal\OpenGL\FbFragShader.glsl">
|
||||||
|
<LogicalName>GlFbFragShader</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
@ -13,281 +9,25 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
public class GLScreen : GameWindow
|
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 Switch Ns;
|
||||||
|
|
||||||
private IGalRenderer Renderer;
|
private IGalRenderer Renderer;
|
||||||
|
|
||||||
private ScreenTexture ScreenTex;
|
|
||||||
|
|
||||||
public GLScreen(Switch Ns, IGalRenderer Renderer)
|
public GLScreen(Switch Ns, IGalRenderer Renderer)
|
||||||
: base(1280, 720,
|
: base(1280, 720,
|
||||||
new GraphicsMode(), "Ryujinx", 0,
|
new GraphicsMode(), "Ryujinx", 0,
|
||||||
DisplayDevice.Default, 3, 3,
|
DisplayDevice.Default, 3, 3,
|
||||||
GraphicsContextFlags.ForwardCompatible)
|
GraphicsContextFlags.ForwardCompatible)
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.Renderer = Renderer;
|
this.Renderer = Renderer;
|
||||||
|
|
||||||
ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLoad(EventArgs e)
|
protected override void OnLoad(EventArgs e)
|
||||||
{
|
{
|
||||||
VSync = VSyncMode.On;
|
VSync = VSyncMode.On;
|
||||||
|
|
||||||
CreateShaders();
|
Renderer.InitializeFrameBuffer();
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||||
|
@ -382,12 +122,7 @@ void main(void) {
|
||||||
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||||
|
|
||||||
RenderFb();
|
|
||||||
|
|
||||||
GL.UseProgram(PrgShaderHandle);
|
|
||||||
|
|
||||||
Renderer.RunActions();
|
Renderer.RunActions();
|
||||||
Renderer.BindTexture(0);
|
|
||||||
Renderer.Render();
|
Renderer.Render();
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
|
@ -395,16 +130,7 @@ void main(void) {
|
||||||
|
|
||||||
protected override void OnResize(EventArgs e)
|
protected override void OnResize(EventArgs e)
|
||||||
{
|
{
|
||||||
GL.UseProgram(PrgShaderHandle);
|
Renderer.SetWindowSize(Width, Height);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue