diff --git a/Assets/Plugins/RetroUnity/Scripts/CoreDownloader.cs b/Assets/Plugins/RetroUnity/Scripts/CoreDownloader.cs index b3a4fdf..8fa79db 100644 --- a/Assets/Plugins/RetroUnity/Scripts/CoreDownloader.cs +++ b/Assets/Plugins/RetroUnity/Scripts/CoreDownloader.cs @@ -31,7 +31,7 @@ static void DownloadCores() { var coreNames = new List() { - "snes9x","blastem", "nestopia" + "snes9x","blastem", "nestopia", "mgba" }; foreach (var coreName in coreNames) { diff --git a/Assets/Plugins/RetroUnity/Scripts/GraphicsProcessor.cs b/Assets/Plugins/RetroUnity/Scripts/GraphicsProcessor.cs new file mode 100644 index 0000000..e887560 --- /dev/null +++ b/Assets/Plugins/RetroUnity/Scripts/GraphicsProcessor.cs @@ -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 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() + }.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() + }.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() + }.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 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 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 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); + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/RetroUnity/Scripts/LibretroWrapper.cs b/Assets/Plugins/RetroUnity/Scripts/LibretroWrapper.cs index 45d75bb..18dafde 100644 --- a/Assets/Plugins/RetroUnity/Scripts/LibretroWrapper.cs +++ b/Assets/Plugins/RetroUnity/Scripts/LibretroWrapper.cs @@ -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. @@ -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; @@ -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. } diff --git a/Packages/manifest.json b/Packages/manifest.json index bf56235..d195c14 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -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",