GDI Render: UI

This commit is contained in:
Blue 2021-01-15 22:59:05 +01:00
parent eac81c8c59
commit 037a647f3a
4 changed files with 47 additions and 102 deletions

View file

@ -7,6 +7,10 @@
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="1.10.0" />
</ItemGroup>

View file

@ -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();
}
}
}

View file

@ -4,12 +4,11 @@ namespace ProjectPSX.Util {
public class DoubleBufferedPanel : Panel {
public DoubleBufferedPanel() {
this.DoubleBuffered = true;
this.BackgroundImageLayout = ImageLayout.Stretch;
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
//this.SetStyle(ControlStyles.UserPaint, true);
this.UpdateStyles();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, false);
UpdateStyles();
}
}
}

View file

@ -11,20 +11,26 @@ using System.Timers;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using ProjectPSX.Interop.Gdi32;
using Gdi32 = ProjectPSX.Interop.Gdi32.NativeMethods;
using System.Runtime.InteropServices;
namespace ProjectPSX {
public class Window : Form, IHostWindow {
const int PSX_MHZ = 33868800;
const int SYNC_CYCLES = 100;
const int MIPS_UNDERCLOCK = 2;
private const int PSX_MHZ = 33868800;
private const int SYNC_CYCLES = 100;
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 _640x480 = new Size(640, 480);
private readonly DoubleBufferedPanel screen = new DoubleBufferedPanel();
private Display display = new Display(640, 480);
private Display vramViewer = new Display(1024, 512);
private GdiBitmap display = new GdiBitmap(1024, 512);
private ProjectPSX psx;
private int fps;
@ -57,7 +63,7 @@ namespace ProjectPSX {
FormBorderStyle = FormBorderStyle.FixedDialog;
KeyUp += new KeyEventHandler(vramViewerToggle);
screen.BackgroundImage = display.Bitmap;// TESTING
screen.Size = _640x480;
screen.Margin = new Padding(0);
screen.MouseDoubleClick += new MouseEventHandler(toggleDebug);
@ -104,8 +110,7 @@ namespace ProjectPSX {
if (cla.Any(s => s.EndsWith(".bin") || s.EndsWith(".cue"))) {
String filename = cla.First(s => s.EndsWith(".bin") || s.EndsWith(".cue"));
return filename;
}
else {
} else {
//Show the user a dialog so they can pick the bin they want to load.
var fileDialog = new OpenFileDialog();
fileDialog.Filter = "BIN/CUE files (*.bin, *.cue)|*.bin;*.cue";
@ -134,15 +139,22 @@ namespace ProjectPSX {
psx.JoyPadDown(button.Value);
}
private void toggleDebug(object sender, MouseEventArgs e) {
psx.toggleDebug();
}
private void toggleDebug(object sender, MouseEventArgs e) => psx.toggleDebug();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Render(int[] vram) {
//Console.WriteLine($"x1 {displayX1} x2 {displayX2} y1 {displayY1} y2 {displayY2}");
int horizontalStart = 0;
int horizontalEnd = horizontalRes;
int verticalStart = 0;
int verticalEnd = verticalRes;
if (isVramViewer) {
Buffer.BlockCopy(vram, 0, display.Bits, 0, 0x200000);
horizontalEnd = 1024;
verticalStart = 0;
verticalEnd = 512;
Marshal.Copy(vram, 0, display.BitmapData, 0x80000);
} else if (is24BitDepth) {
blit24bpp(vram);
} else {
@ -150,7 +162,12 @@ namespace ProjectPSX {
}
fps++;
screen.Invalidate();
using var deviceContext = new GdiDeviceContext(screen.Handle);
Gdi32.StretchBlt(deviceContext, 0, 0, screen.Width, screen.Height,
display.DeviceContext, horizontalStart, verticalStart, horizontalEnd, verticalEnd,
RasterOp.SRCCOPY);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -185,10 +202,11 @@ namespace ProjectPSX {
int p0rgb24bpp = p0R << 16 | p0G << 8 | p0B;
int p1rgb24bpp = p1R << 16 | p1G << 8 | p1B;
display.Bits[x + (y * horizontalRes)] = p0rgb24bpp;
display.Bits[x + 1 + (y * horizontalRes)] = p1rgb24bpp;
display.DrawPixel(x, y, p0rgb24bpp);
display.DrawPixel(x + 1, y, p1rgb24bpp);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -202,12 +220,12 @@ namespace ProjectPSX {
else yRangeOffset = range;
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)];
display.Bits[x + (y * horizontalRes)] = pixel;
//Console.WriteLine(y + " " + x);
display.DrawPixel(x, y, pixel);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -234,18 +252,11 @@ namespace ProjectPSX {
this.verticalRes = verticalRes;
//Console.WriteLine($"setDisplayMode {horizontalRes} {verticalRes} {is24BitDepth}");
if (!isVramViewer) {
display = new Display(horizontalRes, verticalRes);
screen.BackgroundImage = display.Bitmap;
}
}
}
public void SetVRAMStart(ushort displayVRAMXStart, ushort displayVRAMYStart) {
//if (isVramViewer) return;
this.displayVRAMXStart = displayVRAMXStart;
this.displayVRAMYStart = displayVRAMYStart;
@ -253,8 +264,6 @@ namespace ProjectPSX {
}
public void SetVerticalRange(ushort displayY1, ushort displayY2) {
//if (isVramViewer) return;
this.displayY1 = displayY1;
this.displayY2 = displayY2;
@ -262,25 +271,20 @@ namespace ProjectPSX {
}
public void SetHorizontalRange(ushort displayX1, ushort displayX2) {
//if (isVramViewer) return;
this.displayX1 = displayX1;
this.displayX2 = 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 (!isVramViewer) {
display = vramViewer;
screen.Size = vramSize;
} else {
display = new Display(horizontalRes, verticalRes);
screen.Size = _640x480;
}
isVramViewer = !isVramViewer;
screen.BackgroundImage = display.Bitmap;
}
}
@ -294,7 +298,7 @@ namespace ProjectPSX {
}
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;
}
@ -309,9 +313,6 @@ namespace ProjectPSX {
try {
while (true) {
psx.RunFrame();
int cyclesPerFrame = PSX_MHZ / 60;
int syncLoops = (cyclesPerFrame / (SYNC_CYCLES * MIPS_UNDERCLOCK)) + 1;
int cycles = syncLoops * SYNC_CYCLES;
cpuCyclesCounter += cycles;
}
} catch (Exception e) {