From 037a647f3a84b400f6d4b845b430a4d045335319 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 15 Jan 2021 22:59:05 +0100 Subject: [PATCH] GDI Render: UI --- .../ProjectPSX.WinForms.csproj | 4 + ProjectPSX.WinForms/UI/Display.cs | 59 --------------- ProjectPSX.WinForms/UI/DoubleBufferedPanel.cs | 11 ++- ProjectPSX.WinForms/UI/Window.cs | 75 ++++++++++--------- 4 files changed, 47 insertions(+), 102 deletions(-) delete mode 100644 ProjectPSX.WinForms/UI/Display.cs diff --git a/ProjectPSX.WinForms/ProjectPSX.WinForms.csproj b/ProjectPSX.WinForms/ProjectPSX.WinForms.csproj index 040e1c1..f5a0db8 100644 --- a/ProjectPSX.WinForms/ProjectPSX.WinForms.csproj +++ b/ProjectPSX.WinForms/ProjectPSX.WinForms.csproj @@ -7,6 +7,10 @@ true + + true + + diff --git a/ProjectPSX.WinForms/UI/Display.cs b/ProjectPSX.WinForms/UI/Display.cs deleted file mode 100644 index 7e2b90e..0000000 --- a/ProjectPSX.WinForms/UI/Display.cs +++ /dev/null @@ -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(); - } - } -} \ No newline at end of file diff --git a/ProjectPSX.WinForms/UI/DoubleBufferedPanel.cs b/ProjectPSX.WinForms/UI/DoubleBufferedPanel.cs index 9b75701..33b3d8e 100644 --- a/ProjectPSX.WinForms/UI/DoubleBufferedPanel.cs +++ b/ProjectPSX.WinForms/UI/DoubleBufferedPanel.cs @@ -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(); } } } diff --git a/ProjectPSX.WinForms/UI/Window.cs b/ProjectPSX.WinForms/UI/Window.cs index 2519cd4..c0707ea 100644 --- a/ProjectPSX.WinForms/UI/Window.cs +++ b/ProjectPSX.WinForms/UI/Window.cs @@ -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) {