DS - rethink ScreenArranger, fix side by side options
This commit is contained in:
parent
730156b5db
commit
0aaf2b4151
|
@ -1,64 +1,82 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public unsafe class VideoScreen
|
||||
{
|
||||
public VideoScreen(int* buffer, int width, int height)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public int* Buffer { get; }
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
|
||||
public int Length => Width * Height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a way to arrange displays inside a frame buffer.
|
||||
/// </summary>
|
||||
public class ScreenArranger
|
||||
public static class ScreenArranger
|
||||
{
|
||||
private readonly Size[] _sizes;
|
||||
|
||||
public ScreenArranger(Size[] sizes)
|
||||
// TODO: pad lines
|
||||
// TOOD: pass in int[] to reuse buffer
|
||||
public static unsafe int[] Stack(VideoScreen screen1, VideoScreen screen2, int padLines)
|
||||
{
|
||||
_sizes = sizes;
|
||||
}
|
||||
|
||||
public ScreenLayoutSettings LayoutSettings { get; set; }
|
||||
|
||||
public unsafe int[] GenerateFramebuffer(int*[] src, int[] srcLength)
|
||||
{
|
||||
if (src.Length != LayoutSettings.Locations.Length)
|
||||
var ret = new int[screen1.Width * (screen1.Height + screen2.Height)];
|
||||
for (int i = 0; i < screen1.Length; i++)
|
||||
{
|
||||
throw new InvalidCastException("Buffer length mismatch");
|
||||
ret[i] = screen1.Buffer[i];
|
||||
}
|
||||
|
||||
var ret = new int[LayoutSettings.FinalSize.Width * LayoutSettings.FinalSize.Height];
|
||||
for (int iBuf = 0; iBuf < src.Length; iBuf++)
|
||||
for (int i = 0; i < screen2.Length; i++)
|
||||
{
|
||||
int screen = LayoutSettings.Order[iBuf];
|
||||
Size size = _sizes[screen];
|
||||
Point position = LayoutSettings.Locations[screen];
|
||||
ret[screen1.Length + i] = screen2.Buffer[i];
|
||||
}
|
||||
|
||||
int minSrcX = Math.Max(-position.X, 0);
|
||||
int maxSrcX = Math.Min(LayoutSettings.FinalSize.Width - position.X, size.Width);
|
||||
int minDstX = Math.Max(position.X, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int minSrcY = Math.Max(-position.Y, 0);
|
||||
int maxSrcY = Math.Min(LayoutSettings.FinalSize.Height - position.Y, size.Height);
|
||||
int minDstY = Math.Max(position.Y, 0);
|
||||
// Simply populates a buffer with a single screen
|
||||
public static unsafe int[] Copy(VideoScreen screen1)
|
||||
{
|
||||
var ret = new int[screen1.Length];
|
||||
|
||||
if ((maxSrcX - 1) + (maxSrcY - 1) * size.Width > srcLength[iBuf])
|
||||
throw new ArgumentException("The given source buffer is smaller than expected.");
|
||||
for (int i = 0; i < ret.Length; i++)
|
||||
{
|
||||
ret[i] = screen1.Buffer[i];
|
||||
}
|
||||
|
||||
for (int iY = minSrcY; iY < maxSrcY; iY++)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: pass in int[] to reuse buffer
|
||||
// TODO: there is a simpler algorithm for sure
|
||||
public static unsafe int[] SideBySide(VideoScreen screen1, VideoScreen screen2)
|
||||
{
|
||||
int width = screen1.Width + screen2.Width;
|
||||
int height = screen2.Height;
|
||||
var ret = new int[width * height];
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int dstIndex = minDstX + (minDstY + iY - minSrcY) * LayoutSettings.FinalSize.Width;
|
||||
int srcIndex = minSrcX + iY * size.Width;
|
||||
for (int iX = minSrcX; iX < maxSrcX; iX++)
|
||||
ret[dstIndex++] = src[screen][srcIndex++];
|
||||
if (x < screen1.Width)
|
||||
{
|
||||
ret[(y * width) + x] = screen1.Buffer[(y * width / 2) + x];
|
||||
}
|
||||
else
|
||||
{
|
||||
ret[(y * width) + x] = screen2.Buffer[(y * width / 2) + x];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class ScreenLayoutSettings
|
||||
{
|
||||
public Point[] Locations { get; set; }
|
||||
public int[] Order { get; set; }
|
||||
public Size FinalSize { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
throw new Exception("Failed to init NDS.");
|
||||
InitMemoryDomains();
|
||||
|
||||
_screenArranger = new ScreenArranger(new[] { new Size(NativeWidth, NativeHeight), new Size(NativeWidth, NativeHeight) });
|
||||
PutSettings(settings as MelonSettings);
|
||||
PutSyncSettings(syncsettings as MelonSyncSettings);
|
||||
|
||||
|
@ -119,9 +118,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
}
|
||||
}
|
||||
|
||||
public Point? TouchScreenStart => _screenArranger.LayoutSettings.Locations.Length > 1
|
||||
? _screenArranger.LayoutSettings.Locations[1]
|
||||
: (Point?)null;
|
||||
public Point? TouchScreenStart => _settings.ScreenOptions.TouchScreenStart();
|
||||
|
||||
/// <summary>
|
||||
/// MelonDS expects bios and firmware files at a specific location.
|
||||
|
|
|
@ -32,8 +32,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
public bool PutSettings(MelonSettings o)
|
||||
{
|
||||
_settings = o ?? new MelonSettings();
|
||||
_screenArranger.LayoutSettings = _settings.ScreenOptions.ToLayout();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
|
@ -17,8 +16,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
public int VirtualWidth => BufferWidth;
|
||||
public int VirtualHeight => BufferHeight;
|
||||
|
||||
public int BufferWidth => _screenArranger.LayoutSettings.FinalSize.Width;
|
||||
public int BufferHeight => _screenArranger.LayoutSettings.FinalSize.Height;
|
||||
public int BufferWidth => _settings.ScreenOptions.Width();
|
||||
public int BufferHeight => _settings.ScreenOptions.Height();
|
||||
|
||||
public int VsyncNumerator => 60;
|
||||
|
||||
|
@ -26,8 +25,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
|
||||
public int BackgroundColor => 0;
|
||||
|
||||
private readonly ScreenArranger _screenArranger;
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int* GetTopScreenBuffer();
|
||||
[DllImport(dllPath)]
|
||||
|
@ -42,18 +39,21 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
{
|
||||
if (_getNewBuffer)
|
||||
{
|
||||
// Shenanigans
|
||||
var buffers = _settings.ScreenOptions.NeedsBottomScreen()
|
||||
? new[] {GetTopScreenBuffer(), GetBottomScreenBuffer()}
|
||||
: new[] {GetTopScreenBuffer()};
|
||||
|
||||
_getNewBuffer = false;
|
||||
|
||||
|
||||
int bufferSize = GetScreenBufferSize();
|
||||
_buffer = _screenArranger.GenerateFramebuffer(buffers, new[] { bufferSize, bufferSize });
|
||||
_buffer = _settings.ScreenOptions switch
|
||||
{
|
||||
VideoScreenOptions.TopOnly => ScreenArranger.Copy(TopScreen),
|
||||
VideoScreenOptions.SideBySideLR => ScreenArranger.SideBySide(TopScreen, BottomScreen),
|
||||
VideoScreenOptions.SideBySideRL => ScreenArranger.SideBySide(BottomScreen, TopScreen),
|
||||
_ => ScreenArranger.Stack(TopScreen, BottomScreen, 0)
|
||||
};
|
||||
}
|
||||
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
private VideoScreen TopScreen => new VideoScreen(GetTopScreenBuffer(), NativeWidth, NativeHeight);
|
||||
private VideoScreen BottomScreen => new VideoScreen(GetBottomScreenBuffer(), NativeWidth, NativeHeight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Drawing;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
|
@ -10,49 +9,45 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
|
||||
public static class VideoScreenOptionExtensions
|
||||
{
|
||||
public static bool NeedsBottomScreen(this VideoScreenOptions option)
|
||||
public static Point? TouchScreenStart(this VideoScreenOptions option)
|
||||
{
|
||||
return option != VideoScreenOptions.TopOnly;
|
||||
}
|
||||
|
||||
public static ScreenLayoutSettings ToLayout(this VideoScreenOptions option)
|
||||
{
|
||||
return option switch
|
||||
switch (option)
|
||||
{
|
||||
VideoScreenOptions.Default => Default,
|
||||
VideoScreenOptions.TopOnly => TopOnly,
|
||||
VideoScreenOptions.SideBySideLR => SideBySideLR,
|
||||
VideoScreenOptions.SideBySideRL => SideBySideRL,
|
||||
_ => Default
|
||||
};
|
||||
default:
|
||||
return new Point(0, MelonDS.NativeHeight);
|
||||
case VideoScreenOptions.TopOnly:
|
||||
return null;
|
||||
case VideoScreenOptions.SideBySideLR:
|
||||
return new Point(MelonDS.NativeWidth, 0);
|
||||
case VideoScreenOptions.SideBySideRL:
|
||||
return new Point(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static ScreenLayoutSettings Default => new ScreenLayoutSettings
|
||||
public static int Width(this VideoScreenOptions option)
|
||||
{
|
||||
Locations = new[] { new Point(0, 0), new Point(0, MelonDS.NativeHeight) },
|
||||
Order = new[] { 0, 1 },
|
||||
FinalSize = new Size(MelonDS.NativeWidth, MelonDS.NativeHeight * 2)
|
||||
};
|
||||
switch (option)
|
||||
{
|
||||
default:
|
||||
return MelonDS.NativeWidth;
|
||||
case VideoScreenOptions.SideBySideLR:
|
||||
case VideoScreenOptions.SideBySideRL:
|
||||
return MelonDS.NativeWidth * 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static ScreenLayoutSettings TopOnly => new ScreenLayoutSettings
|
||||
// TODO: padding
|
||||
public static int Height(this VideoScreenOptions option)
|
||||
{
|
||||
Locations = new[] { new Point(0, 0) },
|
||||
Order = new[] { 0 },
|
||||
FinalSize = new Size(MelonDS.NativeWidth, MelonDS.NativeHeight)
|
||||
};
|
||||
|
||||
private static ScreenLayoutSettings SideBySideLR => new ScreenLayoutSettings
|
||||
{
|
||||
Locations = new[] { new Point(0, 0), new Point(MelonDS.NativeWidth, MelonDS.NativeHeight) },
|
||||
Order = new[] { 0, 1 },
|
||||
FinalSize = new Size(MelonDS.NativeWidth * 2, MelonDS.NativeHeight)
|
||||
};
|
||||
|
||||
private static ScreenLayoutSettings SideBySideRL => new ScreenLayoutSettings
|
||||
{
|
||||
Locations = new[] {new Point(MelonDS.NativeWidth, MelonDS.NativeHeight), new Point(0, 0) },
|
||||
Order = new[] { 0, 1 },
|
||||
FinalSize = new Size(MelonDS.NativeWidth * 2, MelonDS.NativeHeight)
|
||||
};
|
||||
switch (option)
|
||||
{
|
||||
default:
|
||||
return MelonDS.NativeHeight * 2;
|
||||
case VideoScreenOptions.TopOnly:
|
||||
case VideoScreenOptions.SideBySideLR:
|
||||
case VideoScreenOptions.SideBySideRL:
|
||||
return MelonDS.NativeHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue