Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/graphics processor #20

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Assets/Plugins/RetroUnity/Scripts/CoreDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static void DownloadCores()
{
var coreNames = new List<string>()
{
"snes9x","blastem", "nestopia"
"snes9x","blastem", "nestopia", "mgba"
};
foreach (var coreName in coreNames)
{
Expand Down
177 changes: 177 additions & 0 deletions Assets/Plugins/RetroUnity/Scripts/GraphicsProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;

namespace RetroUnity
{
public class GraphicsProcessor
{
public Action<Texture2D> OnTextureRecreated;

public Texture2D Texture { get; private set; } = new Texture2D(8, 8, TextureFormat.BGRA32, false) { filterMode = FilterMode.Point };
public FilterMode VideoFilterMode
{
get => _filterMode;
set
{
_filterMode = value;
CreateTexture(Texture.width, Texture.height);
}
}

private FilterMode _filterMode = FilterMode.Point;

public unsafe void ProcessFrame0RGB1555(ushort* data, int width, int height, int pitch)
{
CreateTexture(width, height);

new ProcessFrame0RGB1555Job
{
SourceData = data,
Width = width,
Height = height,
PitchPixels = pitch / sizeof(ushort),
TextureData = Texture.GetRawTextureData<uint>()
}.Schedule().Complete();

Texture.Apply();
}

public unsafe void ProcessFrameXRGB8888(uint* data, int width, int height, int pitch)
{
CreateTexture(width, height);

new ProcessFrameXRGB8888Job
{
SourceData = data,
Width = width,
Height = height,
PitchPixels = pitch / sizeof(uint),
TextureData = Texture.GetRawTextureData<uint>()
}.Schedule().Complete();

Texture.Apply();
}

public unsafe void ProcessFrameRGB565(ushort* data, int width, int height, int pitch)
{
CreateTexture(width, height);

new ProcessFrameRGB565Job
{
SourceData = data,
Width = width,
Height = height,
PitchPixels = pitch / sizeof(ushort),
TextureData = Texture.GetRawTextureData<uint>()
}.Schedule().Complete();

Texture.Apply();
}

private void CreateTexture(int width, int height)
{
if (Texture.width != width || Texture.height != height || Texture.filterMode != VideoFilterMode)
{
Texture = new Texture2D(width, height, TextureFormat.BGRA32, false)
{
filterMode = VideoFilterMode
};
OnTextureRecreated?.Invoke(Texture);
}
}

[BurstCompile]
private unsafe struct ProcessFrame0RGB1555Job : IJob
{
[ReadOnly] [NativeDisableUnsafePtrRestriction] public ushort* SourceData;
public int Width;
public int Height;
public int PitchPixels;
[WriteOnly] public NativeArray<uint> TextureData;

public void Execute()
{
ushort* line = SourceData;
for (int y = 0; y < Height; ++y)
{
for (int x = 0; x < Width; ++x)
{
TextureData[y * Width + x] = ARGB1555toBGRA32(line[x]);
}
line += PitchPixels;
}
}
}

[BurstCompile]
private unsafe struct ProcessFrameXRGB8888Job : IJob
{
[ReadOnly] [NativeDisableUnsafePtrRestriction] public uint* SourceData;
public int Width;
public int Height;
public int PitchPixels;
[WriteOnly] public NativeArray<uint> TextureData;

public void Execute()
{
uint* line = SourceData;
for (int y = 0; y < Height; ++y)
{
for (int x = 0; x < Width; ++x)
{
TextureData[y * Width + x] = line[x];
}
line += PitchPixels;
}
}
}

[BurstCompile]
private unsafe struct ProcessFrameRGB565Job : IJob
{
[ReadOnly] [NativeDisableUnsafePtrRestriction] public ushort* SourceData;
public int Width;
public int Height;
public int PitchPixels;
[WriteOnly] public NativeArray<uint> TextureData;

public void Execute()
{
ushort* line = SourceData;
for (int y = 0; y < Height; ++y)
{
for (int x = 0; x < Width; ++x)
{
TextureData[y * Width + x] = RGB565toBGRA32(line[x]);
}
line += PitchPixels;
}
}
}

private static uint ARGB1555toBGRA32(ushort packed)
{
uint a = (uint)packed & 0x8000;
uint r = (uint)packed & 0x7C00;
uint g = (uint)packed & 0x03E0;
uint b = (uint)packed & 0x1F;
uint rgb = (r << 9) | (g << 6) | (b << 3);
return (a * 0x1FE00) | rgb | ((rgb >> 5) & 0x070707);
}

private static uint RGB565toBGRA32(ushort packed)
{
uint r = ((uint)packed >> 11) & 0x1f;
uint g = ((uint)packed >> 5) & 0x3f;
uint b = ((uint)packed >> 0) & 0x1f;
r = (r << 3) | (r >> 2);
g = (g << 2) | (g >> 4);
b = (b << 3) | (b >> 2);
return (0xffu << 24) | (r << 16) | (g << 8) | (b << 0);
}
}
}
157 changes: 23 additions & 134 deletions Assets/Plugins/RetroUnity/Scripts/LibretroWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ public class LibretroWrapper : MonoBehaviour {
private static Speaker _speaker;

public static Texture2D tex;
public static int pix;
public static int w;
public static int h;
public static int p;

public static byte[] Src;
public static byte[] Dst;

public enum PixelFormat {
// 0RGB1555, native endian. 0 bit must be set to 0.
Expand Down Expand Up @@ -134,13 +127,6 @@ public class Wrapper {
private PixelFormat _pixelFormat;
private bool _requiresFullPath;
private SystemAVInfo _av;
private Pixel[] _frameBuffer;
public static int Pix = 0;
public static int w = 0;
public static int h = 0;
public static int p = 0;
public static uint Button;
public static uint Keep;

public IDLLHandler DLLHandler;

Expand Down Expand Up @@ -202,130 +188,33 @@ public SystemAVInfo GetAVInfo() {
return _av;
}

public Pixel[] GetFramebuffer() {
return _frameBuffer;
}
private GraphicsProcessor graphicsProcessor = new GraphicsProcessor();

private unsafe void RetroVideoRefresh(void* data, uint width, uint height, uint pitch) {

// Process Pixels one by one for now...this is not the best way to do it
// should be using memory streams or something

//Declare the pixel buffer to pass on to the renderer
if(_frameBuffer == null || _frameBuffer.Length != width * height)
_frameBuffer = new Pixel[width * height];

//Get the array from unmanaged memory as a pointer
var pixels = (IntPtr)data;
//Gets The pointer to the row start to use with the pitch
//IntPtr rowStart = pixels;

//Get the size to move the pointer
//int size = 0;

uint i;
uint j;

switch (_pixelFormat) {
case PixelFormat.RetroPixelFormat_0RGB1555:

LibretroWrapper.w = Convert.ToInt32(width);
LibretroWrapper.h = Convert.ToInt32(height);
if (tex == null) {
tex = new Texture2D(LibretroWrapper.w, LibretroWrapper.h, TextureFormat.RGB565, false);
}
LibretroWrapper.p = Convert.ToInt32(pitch);

//size = Marshal.SizeOf(typeof(short));
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
short packed = Marshal.ReadInt16(pixels);
_frameBuffer[i * width + j] = new Pixel {
Alpha = 1
,
Red = ((packed >> 10) & 0x001F) / 31.0f
,
Green = ((packed >> 5) & 0x001F) / 31.0f
,
Blue = (packed & 0x001F) / 31.0f
};
var color = new Color(((packed >> 10) & 0x001F) / 31.0f, ((packed >> 5) & 0x001F) / 31.0f, (packed & 0x001F) / 31.0f, 1.0f);
tex.SetPixel((int)i, (int)j, color);
//pixels = (IntPtr)((int)pixels + size);
}
tex.filterMode = FilterMode.Trilinear;
tex.Apply();
//pixels = (IntPtr)((int)rowStart + pitch);
//rowStart = pixels;
}
break;
case PixelFormat.RetroPixelFormatXRGB8888:

LibretroWrapper.w = Convert.ToInt32(width);
LibretroWrapper.h = Convert.ToInt32(height);
if (tex == null) {
tex = new Texture2D(LibretroWrapper.w, LibretroWrapper.h, TextureFormat.RGB565, false);
}
LibretroWrapper.p = Convert.ToInt32(pitch);

//size = Marshal.SizeOf(typeof(int));
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
int packed = Marshal.ReadInt32(pixels);
_frameBuffer[i * width + j] = new Pixel {
Alpha = 1,
Red = ((packed >> 16) & 0x00FF) / 255.0f,
Green = ((packed >> 8) & 0x00FF) / 255.0f,
Blue = (packed & 0x00FF) / 255.0f
};
var color = new Color(((packed >> 16) & 0x00FF) / 255.0f, ((packed >> 8) & 0x00FF) / 255.0f, (packed & 0x00FF) / 255.0f, 1.0f);
tex.SetPixel((int)i, (int)j, color);
//pixels = (IntPtr)((int)pixels + size);
}
//pixels = (IntPtr)((int)rowStart + pitch);
//rowStart = pixels;

}

tex.filterMode = FilterMode.Trilinear;
tex.Apply();
break;

case PixelFormat.RetroPixelFormatRGB565:

var imagedata565 = new IntPtr(data);
LibretroWrapper.w = Convert.ToInt32(width);
LibretroWrapper.h = Convert.ToInt32(height);
if (tex == null) {
tex = new Texture2D(LibretroWrapper.w, LibretroWrapper.h, TextureFormat.RGB565, false);
}
LibretroWrapper.p = Convert.ToInt32(pitch);
int srcsize565 = 2 * (LibretroWrapper.p * LibretroWrapper.h);
int dstsize565 = 2 * (LibretroWrapper.w * LibretroWrapper.h);
if (Src == null || Src.Length != srcsize565)
Src = new byte[srcsize565];
if (Dst == null || Dst.Length != dstsize565)
Dst = new byte[dstsize565];
Marshal.Copy(imagedata565, Src, 0, srcsize565);
int m565 = 0;
for (int y = 0; y < LibretroWrapper.h; y++) {
for (int k = 0 * 2 + y * LibretroWrapper.p; k < LibretroWrapper.w * 2 + y * LibretroWrapper.p; k++) {
Dst[m565] = Src[k];
m565++;
}
}
tex.LoadRawTextureData(Dst);
tex.filterMode = FilterMode.Trilinear;
tex.Apply();
break;
case PixelFormat.RetroPixelFormatUnknown:
_frameBuffer = null;
break;
default:
throw new ArgumentOutOfRangeException();
if (graphicsProcessor != null)
{
int intWidth = (int) width;
int intHeight = (int) height;
int intPitch = (int) pitch;

tex = graphicsProcessor.Texture;
switch (_pixelFormat)
{
case PixelFormat.RetroPixelFormat_0RGB1555:
graphicsProcessor.ProcessFrame0RGB1555((ushort*) data, intWidth, intHeight, intPitch);
break;
case PixelFormat.RetroPixelFormatXRGB8888:
graphicsProcessor.ProcessFrameXRGB8888((uint*) data, intWidth, intHeight, intPitch);
break;
case PixelFormat.RetroPixelFormatRGB565:
graphicsProcessor.ProcessFrameRGB565((ushort*) data, intWidth, intHeight, intPitch);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}

private void RetroAudioSample(short left, short right) {
// Unused.
}
Expand Down
1 change: 1 addition & 0 deletions Packages/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"com.unity.burst": "1.3.6",
"com.unity.collab-proxy": "1.2.16",
"com.unity.ide.rider": "1.1.4",
"com.unity.ide.vscode": "1.2.1",
Expand Down