mirror of
https://github.com/BluestormDNA/ProjectPSX.git
synced 2025-04-02 10:52:34 -04:00
Gdi (#18)
* Push Interop * GDI Render: UI * Gdi: Add BltMode * Winform GDI: Handle y ranges This is badly handled and X still left
This commit is contained in:
parent
8912d88da4
commit
2f79a50d36
9 changed files with 350 additions and 112 deletions
8
ProjectPSX.WinForms/Interop/ExternDll.cs
Normal file
8
ProjectPSX.WinForms/Interop/ExternDll.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace ProjectPSX
|
||||||
|
{
|
||||||
|
internal static class ExternDll
|
||||||
|
{
|
||||||
|
public const string Gdi32 = "gdi32.dll";
|
||||||
|
public const string User32 = "user32.dll";
|
||||||
|
}
|
||||||
|
}
|
121
ProjectPSX.WinForms/Interop/Gdi32.cs
Normal file
121
ProjectPSX.WinForms/Interop/Gdi32.cs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
|
||||||
|
namespace ProjectPSX.Interop.Gdi32
|
||||||
|
{
|
||||||
|
internal enum BitmapCompression : uint
|
||||||
|
{
|
||||||
|
BI_RGB = 0,
|
||||||
|
BI_RLE8 = 1,
|
||||||
|
BI_RLE4 = 2,
|
||||||
|
BI_BITFIELDS = 3,
|
||||||
|
BI_JPEG = 4,
|
||||||
|
BI_PNG = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct BitmapInfoHeader
|
||||||
|
{
|
||||||
|
public uint biSize;
|
||||||
|
public int biWidth;
|
||||||
|
public int biHeight;
|
||||||
|
public ushort biPlanes;
|
||||||
|
public ushort biBitCount;
|
||||||
|
public BitmapCompression biCompression;
|
||||||
|
public uint biSizeImage;
|
||||||
|
public int biXPelsPerMeter;
|
||||||
|
public int biYPelsPerMeter;
|
||||||
|
public uint biClrUsed;
|
||||||
|
public uint biClrImportant;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct RgbQuad
|
||||||
|
{
|
||||||
|
public byte rgbBlue;
|
||||||
|
public byte rgbGreen;
|
||||||
|
public byte rgbRed;
|
||||||
|
public byte rgbReserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct BitmapInfo
|
||||||
|
{
|
||||||
|
public BitmapInfoHeader bmiHeader;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||||
|
public uint[] bmiColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum ColorUsage : uint
|
||||||
|
{
|
||||||
|
DIB_RGB_COLORS = 0, /* color table in RGBs */
|
||||||
|
DIB_PAL_COLORS = 1, /* color table in palette indices */
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum RasterOp : uint
|
||||||
|
{
|
||||||
|
SRCCOPY = 0x00CC0020, // dest = source
|
||||||
|
SRCPAINT = 0x00EE0086, // dest = source OR dest
|
||||||
|
SRCAND = 0x008800C6, // dest = source AND dest
|
||||||
|
SRCINVERT = 0x00660046, // dest = source XOR dest
|
||||||
|
SRCERASE = 0x00440328, // dest = source AND (NOT dest )
|
||||||
|
NOTSRCCOPY = 0x00330008, // dest = (NOT source)
|
||||||
|
NOTSRCERASE = 0x001100A6, // dest = (NOT src) AND (NOT dest
|
||||||
|
MERGECOPY = 0x00C000CA, // dest = (source AND pattern)
|
||||||
|
MERGEPAINT = 0x00BB0226, // dest = (NOT source) OR dest
|
||||||
|
PATCOPY = 0x00F00021, // dest = pattern
|
||||||
|
PATPAINT = 0x00FB0A09, // dest = DPSnoo
|
||||||
|
PATINVERT = 0x005A0049, // dest = pattern XOR dest
|
||||||
|
DSTINVERT = 0x00550009, // dest = (NOT dest)
|
||||||
|
BLACKNESS = 0x00000042, // dest = BLACK
|
||||||
|
WHITENESS = 0x00FF0062, // dest = WHITE
|
||||||
|
NOMIRRORBITMAP = 0x80000000, // Do not Mirror the bitmap in this call
|
||||||
|
CAPTUREBLT = 0x40000000, // Include layered windows
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum BltMode : uint {
|
||||||
|
STRETCH_ANDSCANS = 0x01,
|
||||||
|
STRETCH_ORSCANS = 0x02,
|
||||||
|
STRETCH_DELETESCANS = 0x03,
|
||||||
|
STRETCH_HALFTONE = 0x04,
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
internal static class NativeMethods
|
||||||
|
{
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool DeleteDC(IntPtr hdc);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
internal static extern IntPtr CreateDIBSection(IntPtr hdc, [In] in BitmapInfo pbmi, ColorUsage usage,
|
||||||
|
out IntPtr ppvBits, IntPtr hSection, uint offset);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool DeleteObject(IntPtr ho);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool StretchBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest,
|
||||||
|
IntPtr hdcSrc, int xSrc, int ySrc, int wSrc, int hSrc,
|
||||||
|
RasterOp rop);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
internal static extern int SetStretchBltMode(IntPtr hdc, BltMode mode);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.Gdi32)]
|
||||||
|
internal static extern int StretchDIBits(IntPtr hdc, int xDest, int yDest, int DestWidth, int DestHeight,
|
||||||
|
int xSrc, int ySrc, int SrcWidth, int SrcHeight, IntPtr lpBits,
|
||||||
|
[In] ref BitmapInfo lpbmi, ColorUsage iUsage, RasterOp rop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
105
ProjectPSX.WinForms/Interop/GdiBitmap.cs
Normal file
105
ProjectPSX.WinForms/Interop/GdiBitmap.cs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using ProjectPSX.Interop.Gdi32;
|
||||||
|
|
||||||
|
using Gdi32 = ProjectPSX.Interop.Gdi32.NativeMethods;
|
||||||
|
|
||||||
|
namespace ProjectPSX
|
||||||
|
{
|
||||||
|
internal class GdiBitmap : IDisposable
|
||||||
|
{
|
||||||
|
public readonly int BytesPerPixel = 4;
|
||||||
|
|
||||||
|
public readonly IntPtr DeviceContext;
|
||||||
|
public readonly IntPtr BitmapHandle;
|
||||||
|
|
||||||
|
public readonly int Width;
|
||||||
|
public readonly int Height;
|
||||||
|
|
||||||
|
public readonly IntPtr BitmapData;
|
||||||
|
|
||||||
|
private readonly IntPtr _oldObject;
|
||||||
|
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
public GdiBitmap(int width, int height)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
|
||||||
|
DeviceContext = Gdi32.CreateCompatibleDC(IntPtr.Zero);
|
||||||
|
|
||||||
|
var bitmapHeader = new BitmapInfoHeader
|
||||||
|
{
|
||||||
|
biSize = (uint)Marshal.SizeOf<BitmapInfoHeader>(),
|
||||||
|
biWidth = width,
|
||||||
|
biHeight = -height, // negative, top-down bitmap
|
||||||
|
biPlanes = 1,
|
||||||
|
biBitCount = (ushort)(8 * BytesPerPixel),
|
||||||
|
biCompression = BitmapCompression.BI_RGB,
|
||||||
|
};
|
||||||
|
|
||||||
|
var bitmapInfo = new BitmapInfo
|
||||||
|
{
|
||||||
|
bmiHeader = bitmapHeader,
|
||||||
|
};
|
||||||
|
|
||||||
|
BitmapHandle = Gdi32.CreateDIBSection(DeviceContext, in bitmapInfo, ColorUsage.DIB_RGB_COLORS,
|
||||||
|
out BitmapData, IntPtr.Zero, 0);
|
||||||
|
|
||||||
|
_oldObject = Gdi32.SelectObject(DeviceContext, BitmapHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void DrawPixel(int x, int y, int color) {
|
||||||
|
int* pixel = (int*)BitmapData;
|
||||||
|
pixel += x + (y * Width);
|
||||||
|
*pixel = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe int GetPixel(int x, int y) {
|
||||||
|
int* pixel = (int*)BitmapData;
|
||||||
|
pixel += x + (y * Width);
|
||||||
|
return *pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
~GdiBitmap()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code added to correctly implement the disposable pattern.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// TODO: dispose managed state (managed objects).
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_oldObject != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Gdi32.SelectObject(DeviceContext, _oldObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BitmapHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Gdi32.DeleteObject(BitmapHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DeviceContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Gdi32.DeleteDC(DeviceContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
ProjectPSX.WinForms/Interop/GdiDeviceContext.cs
Normal file
28
ProjectPSX.WinForms/Interop/GdiDeviceContext.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using User32 = ProjectPSX.Interop.User32.NativeMethods;
|
||||||
|
|
||||||
|
namespace ProjectPSX
|
||||||
|
{
|
||||||
|
internal readonly ref struct GdiDeviceContext
|
||||||
|
{
|
||||||
|
private readonly IntPtr _handle;
|
||||||
|
private readonly IntPtr _hdc;
|
||||||
|
|
||||||
|
public GdiDeviceContext(IntPtr handle)
|
||||||
|
{
|
||||||
|
_handle = handle;
|
||||||
|
_hdc = User32.GetDC(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
User32.ReleaseDC(_handle, _hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntPtr(GdiDeviceContext dc)
|
||||||
|
{
|
||||||
|
return dc._hdc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
ProjectPSX.WinForms/Interop/User32.cs
Normal file
33
ProjectPSX.WinForms/Interop/User32.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
|
||||||
|
namespace ProjectPSX.Interop.User32
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct Message
|
||||||
|
{
|
||||||
|
public IntPtr hWnd;
|
||||||
|
public uint msg;
|
||||||
|
public IntPtr wParam;
|
||||||
|
public IntPtr lParam;
|
||||||
|
public uint time;
|
||||||
|
public Point p;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
internal static class NativeMethods
|
||||||
|
{
|
||||||
|
[DllImport(ExternDll.User32)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin,
|
||||||
|
uint messageFilterMax, uint flags);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.User32)]
|
||||||
|
internal static extern IntPtr GetDC(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport(ExternDll.User32)]
|
||||||
|
internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,10 @@
|
||||||
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
|
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NAudio" Version="1.10.0" />
|
<PackageReference Include="NAudio" Version="1.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
|
|
||||||
namespace ProjectPSX {
|
|
||||||
public class Display : IDisposable {
|
|
||||||
public Bitmap Bitmap { get; set; }
|
|
||||||
public Int32[] Bits { get; private set; }
|
|
||||||
public bool Disposed { get; private set; }
|
|
||||||
public int Height;
|
|
||||||
public int Width;
|
|
||||||
|
|
||||||
protected GCHandle BitsHandle { get; private set; }
|
|
||||||
|
|
||||||
public Display(int width, int height) {
|
|
||||||
Height = height;
|
|
||||||
Width = width;
|
|
||||||
Bits = new Int32[Width * Height];
|
|
||||||
BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
|
|
||||||
|
|
||||||
Bitmap = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppRgb, BitsHandle.AddrOfPinnedObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetPixel(int x, int y, int color) {
|
|
||||||
int index = x + (y * Width);
|
|
||||||
Bits[index] = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int GetPixelRGB888(int x, int y) {
|
|
||||||
int index = x + (y * Width);
|
|
||||||
return Bits[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ushort GetPixelBGR555(int x, int y) {
|
|
||||||
int index = x + (y * Width);
|
|
||||||
int color = Bits[index];
|
|
||||||
|
|
||||||
byte m = (byte)((color & 0xFF000000) >> 24);
|
|
||||||
byte r = (byte)((color & 0x00FF0000) >> 16 + 3);
|
|
||||||
byte g = (byte)((color & 0x0000FF00) >> 8 + 3);
|
|
||||||
byte b = (byte)((color & 0x000000FF) >> 3);
|
|
||||||
|
|
||||||
return (ushort)(m << 15 | b << 10 | g << 5 | r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
if (Disposed) return;
|
|
||||||
Disposed = true;
|
|
||||||
Bitmap.Dispose();
|
|
||||||
BitsHandle.Free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,12 +4,11 @@ namespace ProjectPSX.Util {
|
||||||
public class DoubleBufferedPanel : Panel {
|
public class DoubleBufferedPanel : Panel {
|
||||||
|
|
||||||
public DoubleBufferedPanel() {
|
public DoubleBufferedPanel() {
|
||||||
this.DoubleBuffered = true;
|
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
this.BackgroundImageLayout = ImageLayout.Stretch;
|
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
SetStyle(ControlStyles.UserPaint, false);
|
||||||
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
|
||||||
//this.SetStyle(ControlStyles.UserPaint, true);
|
UpdateStyles();
|
||||||
this.UpdateStyles();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,20 +11,26 @@ using System.Timers;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using ProjectPSX.Interop.Gdi32;
|
||||||
|
using Gdi32 = ProjectPSX.Interop.Gdi32.NativeMethods;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ProjectPSX {
|
namespace ProjectPSX {
|
||||||
public class Window : Form, IHostWindow {
|
public class Window : Form, IHostWindow {
|
||||||
|
|
||||||
const int PSX_MHZ = 33868800;
|
private const int PSX_MHZ = 33868800;
|
||||||
const int SYNC_CYCLES = 100;
|
private const int SYNC_CYCLES = 100;
|
||||||
const int MIPS_UNDERCLOCK = 2;
|
private const int MIPS_UNDERCLOCK = 2;
|
||||||
|
|
||||||
|
private const int cyclesPerFrame = PSX_MHZ / 60;
|
||||||
|
private const int syncLoops = (cyclesPerFrame / (SYNC_CYCLES * MIPS_UNDERCLOCK)) + 1;
|
||||||
|
private const int cycles = syncLoops * SYNC_CYCLES;
|
||||||
|
|
||||||
private Size vramSize = new Size(1024, 512);
|
private Size vramSize = new Size(1024, 512);
|
||||||
private Size _640x480 = new Size(640, 480);
|
private Size _640x480 = new Size(640, 480);
|
||||||
private readonly DoubleBufferedPanel screen = new DoubleBufferedPanel();
|
private readonly DoubleBufferedPanel screen = new DoubleBufferedPanel();
|
||||||
|
|
||||||
private Display display = new Display(640, 480);
|
private GdiBitmap display = new GdiBitmap(1024, 512);
|
||||||
private Display vramViewer = new Display(1024, 512);
|
|
||||||
|
|
||||||
private ProjectPSX psx;
|
private ProjectPSX psx;
|
||||||
private int fps;
|
private int fps;
|
||||||
|
@ -57,7 +63,6 @@ namespace ProjectPSX {
|
||||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
KeyUp += new KeyEventHandler(vramViewerToggle);
|
KeyUp += new KeyEventHandler(vramViewerToggle);
|
||||||
|
|
||||||
screen.BackgroundImage = display.Bitmap;// TESTING
|
|
||||||
screen.Size = _640x480;
|
screen.Size = _640x480;
|
||||||
screen.Margin = new Padding(0);
|
screen.Margin = new Padding(0);
|
||||||
screen.MouseDoubleClick += new MouseEventHandler(toggleDebug);
|
screen.MouseDoubleClick += new MouseEventHandler(toggleDebug);
|
||||||
|
@ -104,8 +109,7 @@ namespace ProjectPSX {
|
||||||
if (cla.Any(s => s.EndsWith(".bin") || s.EndsWith(".cue"))) {
|
if (cla.Any(s => s.EndsWith(".bin") || s.EndsWith(".cue"))) {
|
||||||
String filename = cla.First(s => s.EndsWith(".bin") || s.EndsWith(".cue"));
|
String filename = cla.First(s => s.EndsWith(".bin") || s.EndsWith(".cue"));
|
||||||
return filename;
|
return filename;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//Show the user a dialog so they can pick the bin they want to load.
|
//Show the user a dialog so they can pick the bin they want to load.
|
||||||
var fileDialog = new OpenFileDialog();
|
var fileDialog = new OpenFileDialog();
|
||||||
fileDialog.Filter = "BIN/CUE files (*.bin, *.cue)|*.bin;*.cue";
|
fileDialog.Filter = "BIN/CUE files (*.bin, *.cue)|*.bin;*.cue";
|
||||||
|
@ -134,15 +138,20 @@ namespace ProjectPSX {
|
||||||
psx.JoyPadDown(button.Value);
|
psx.JoyPadDown(button.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleDebug(object sender, MouseEventArgs e) {
|
private void toggleDebug(object sender, MouseEventArgs e) => psx.toggleDebug();
|
||||||
psx.toggleDebug();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Render(int[] vram) {
|
public void Render(int[] vram) {
|
||||||
|
//Console.WriteLine($"x1 {displayX1} x2 {displayX2} y1 {displayY1} y2 {displayY2}");
|
||||||
|
|
||||||
|
int horizontalEnd = horizontalRes;
|
||||||
|
int verticalEnd = verticalRes;
|
||||||
|
|
||||||
if (isVramViewer) {
|
if (isVramViewer) {
|
||||||
Buffer.BlockCopy(vram, 0, display.Bits, 0, 0x200000);
|
horizontalEnd = 1024;
|
||||||
|
verticalEnd = 512;
|
||||||
|
|
||||||
|
Marshal.Copy(vram, 0, display.BitmapData, 0x80000);
|
||||||
} else if (is24BitDepth) {
|
} else if (is24BitDepth) {
|
||||||
blit24bpp(vram);
|
blit24bpp(vram);
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,16 +159,18 @@ namespace ProjectPSX {
|
||||||
}
|
}
|
||||||
|
|
||||||
fps++;
|
fps++;
|
||||||
screen.Invalidate();
|
|
||||||
|
using var deviceContext = new GdiDeviceContext(screen.Handle);
|
||||||
|
|
||||||
|
Gdi32.StretchBlt(deviceContext, 0, 0, screen.Width, screen.Height,
|
||||||
|
display.DeviceContext, 0, 0, horizontalEnd, verticalEnd,
|
||||||
|
RasterOp.SRCCOPY);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void blit24bpp(int[] vramBits) {
|
private void blit24bpp(int[] vramBits) {
|
||||||
int range = (240 - (displayY2 - displayY1)) / 2;
|
int yRangeOffset = (240 - (displayY2 - displayY1)) >> (verticalRes == 480 ? 0 : 1);
|
||||||
|
if (yRangeOffset < 0) yRangeOffset = 0;
|
||||||
int yRangeOffset;
|
|
||||||
if (range < 0) yRangeOffset = 0;
|
|
||||||
else yRangeOffset = range;
|
|
||||||
|
|
||||||
for (int y = yRangeOffset; y < verticalRes - yRangeOffset; y++) {
|
for (int y = yRangeOffset; y < verticalRes - yRangeOffset; y++) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
@ -185,29 +196,27 @@ namespace ProjectPSX {
|
||||||
int p0rgb24bpp = p0R << 16 | p0G << 8 | p0B;
|
int p0rgb24bpp = p0R << 16 | p0G << 8 | p0B;
|
||||||
int p1rgb24bpp = p1R << 16 | p1G << 8 | p1B;
|
int p1rgb24bpp = p1R << 16 | p1G << 8 | p1B;
|
||||||
|
|
||||||
display.Bits[x + (y * horizontalRes)] = p0rgb24bpp;
|
display.DrawPixel(x, y, p0rgb24bpp);
|
||||||
display.Bits[x + 1 + (y * horizontalRes)] = p1rgb24bpp;
|
display.DrawPixel(x + 1, y, p1rgb24bpp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void blit16bpp(int[] vramBits) {
|
private void blit16bpp(int[] vramBits) {
|
||||||
//Console.WriteLine($"x1 {displayX1} x2 {displayX2} y1 {displayY1} y2 {displayY2}");
|
//Console.WriteLine($"x1 {displayX1} x2 {displayX2} y1 {displayY1} y2 {displayY2}");
|
||||||
//Console.WriteLine($"Display Height {display.Height} Width {display.Width}");
|
//Console.WriteLine($"Display Height {display.Height} Width {display.Width}");
|
||||||
int range = (240 - (displayY2 - displayY1)) / 2;
|
int yRangeOffset = (240 - (displayY2 - displayY1)) >> (verticalRes == 480 ? 0 : 1);
|
||||||
|
if (yRangeOffset < 0) yRangeOffset = 0;
|
||||||
int yRangeOffset;
|
|
||||||
if (range < 0) yRangeOffset = 0;
|
|
||||||
else yRangeOffset = range;
|
|
||||||
|
|
||||||
for (int y = yRangeOffset; y < verticalRes - yRangeOffset; y++) {
|
for (int y = yRangeOffset; y < verticalRes - yRangeOffset; y++) {
|
||||||
for (int x = 0; x < display.Width; x++) {
|
for (int x = 0; x < horizontalRes; x++) {
|
||||||
int pixel = vramBits[(x + displayVRAMXStart) + ((y - yRangeOffset + displayVRAMYStart) * 1024)];
|
int pixel = vramBits[(x + displayVRAMXStart) + ((y - yRangeOffset + displayVRAMYStart) * 1024)];
|
||||||
display.Bits[x + (y * horizontalRes)] = pixel;
|
display.DrawPixel(x, y, pixel);
|
||||||
//Console.WriteLine(y + " " + x);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
@ -233,19 +242,13 @@ namespace ProjectPSX {
|
||||||
this.horizontalRes = horizontalRes;
|
this.horizontalRes = horizontalRes;
|
||||||
this.verticalRes = verticalRes;
|
this.verticalRes = verticalRes;
|
||||||
|
|
||||||
|
clearDisplay();
|
||||||
//Console.WriteLine($"setDisplayMode {horizontalRes} {verticalRes} {is24BitDepth}");
|
//Console.WriteLine($"setDisplayMode {horizontalRes} {verticalRes} {is24BitDepth}");
|
||||||
|
|
||||||
if (!isVramViewer) {
|
|
||||||
display = new Display(horizontalRes, verticalRes);
|
|
||||||
screen.BackgroundImage = display.Bitmap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVRAMStart(ushort displayVRAMXStart, ushort displayVRAMYStart) {
|
public void SetVRAMStart(ushort displayVRAMXStart, ushort displayVRAMYStart) {
|
||||||
//if (isVramViewer) return;
|
|
||||||
|
|
||||||
this.displayVRAMXStart = displayVRAMXStart;
|
this.displayVRAMXStart = displayVRAMXStart;
|
||||||
this.displayVRAMYStart = displayVRAMYStart;
|
this.displayVRAMYStart = displayVRAMYStart;
|
||||||
|
|
||||||
|
@ -253,8 +256,6 @@ namespace ProjectPSX {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVerticalRange(ushort displayY1, ushort displayY2) {
|
public void SetVerticalRange(ushort displayY1, ushort displayY2) {
|
||||||
//if (isVramViewer) return;
|
|
||||||
|
|
||||||
this.displayY1 = displayY1;
|
this.displayY1 = displayY1;
|
||||||
this.displayY2 = displayY2;
|
this.displayY2 = displayY2;
|
||||||
|
|
||||||
|
@ -262,28 +263,29 @@ namespace ProjectPSX {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetHorizontalRange(ushort displayX1, ushort displayX2) {
|
public void SetHorizontalRange(ushort displayX1, ushort displayX2) {
|
||||||
//if (isVramViewer) return;
|
|
||||||
|
|
||||||
this.displayX1 = displayX1;
|
this.displayX1 = displayX1;
|
||||||
this.displayX2 = displayX2;
|
this.displayX2 = displayX2;
|
||||||
|
|
||||||
//Console.WriteLine($"Horizontal Range {displayX1} {displayX2}");
|
//Console.WriteLine($"Horizontal Range {displayX1} {displayX2}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void vramViewerToggle(object sender, KeyEventArgs e) { //this is very buggy but its only for debug purposes maybe disable it when unneded?
|
private void vramViewerToggle(object sender, KeyEventArgs e) {
|
||||||
if(e.KeyCode == Keys.Tab) {
|
if(e.KeyCode == Keys.Tab) {
|
||||||
if (!isVramViewer) {
|
if (!isVramViewer) {
|
||||||
display = vramViewer;
|
|
||||||
screen.Size = vramSize;
|
screen.Size = vramSize;
|
||||||
} else {
|
} else {
|
||||||
display = new Display(horizontalRes, verticalRes);
|
|
||||||
screen.Size = _640x480;
|
screen.Size = _640x480;
|
||||||
}
|
}
|
||||||
isVramViewer = !isVramViewer;
|
isVramViewer = !isVramViewer;
|
||||||
screen.BackgroundImage = display.Bitmap;
|
clearDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe void clearDisplay() {
|
||||||
|
Span<uint> span = new Span<uint>(display.BitmapData.ToPointer(), 0x80000);
|
||||||
|
span.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public void Play(byte[] samples) {
|
public void Play(byte[] samples) {
|
||||||
buffer.AddSamples(samples, 0, samples.Length);
|
buffer.AddSamples(samples, 0, samples.Length);
|
||||||
|
|
||||||
|
@ -294,7 +296,7 @@ namespace ProjectPSX {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTimedEvent(object sender, ElapsedEventArgs e) {
|
private void OnTimedEvent(object sender, ElapsedEventArgs e) {
|
||||||
Text = $"ProjectPSX | Cpu Speed {(int)((float)cpuCyclesCounter / (PSX_MHZ / MIPS_UNDERCLOCK) * SYNC_CYCLES)}% | Vps {GetVPS()}";
|
Text = $"ProjectPSX | Cpu {(int)((float)cpuCyclesCounter / (PSX_MHZ / MIPS_UNDERCLOCK) * SYNC_CYCLES)}% | Vps {GetVPS()}";
|
||||||
cpuCyclesCounter = 0;
|
cpuCyclesCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,9 +311,6 @@ namespace ProjectPSX {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
psx.RunFrame();
|
psx.RunFrame();
|
||||||
int cyclesPerFrame = PSX_MHZ / 60;
|
|
||||||
int syncLoops = (cyclesPerFrame / (SYNC_CYCLES * MIPS_UNDERCLOCK)) + 1;
|
|
||||||
int cycles = syncLoops * SYNC_CYCLES;
|
|
||||||
cpuCyclesCounter += cycles;
|
cpuCyclesCounter += cycles;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue