GB: add "GPU Viewer", similar to other gpu debugging tools. doesn't do much yet.

This commit is contained in:
goyuken 2012-11-05 01:34:11 +00:00
parent b04fd0d422
commit 271d4102e8
8 changed files with 490 additions and 1 deletions

View File

@ -145,6 +145,16 @@ namespace BizHawk.Emulation.Consoles.GB
tracecb = null;
LibGambatte.gambatte_settracecallback(GambatteState, tracecb);
// todo: have the gambatte core actually call this at an appropriate time
if (scanlinecallback != null)
{
IntPtr vram = IntPtr.Zero;
int vramlength = 0;
if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref vram, ref vramlength))
throw new Exception();
scanlinecallback(vram, vramlength, LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
}
LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp);
// upload any modified data to the memory domains
@ -575,6 +585,34 @@ namespace BizHawk.Emulation.Consoles.GB
#endregion
#region ppudebug
/// <summary>
/// a callback to be registered at a particular scanline
/// </summary>
/// <param name="vram"></param>
/// <param name="vramlength">length of vram in bytes</param>
/// <param name="lcdc">current LCDC status</param>
public delegate void ScanlineCallback(IntPtr vram, int vramlength, int lcdc);
ScanlineCallback scanlinecallback;
/// <summary>
/// set up callback
/// </summary>
/// <param name="callback"></param>
/// <param name="line">scanline</param>
public void SetScanlineCallback(ScanlineCallback callback, int line)
{
if (callback == null)
this.scanlinecallback = null;
else if (line < 0 || line > 153)
throw new ArgumentOutOfRangeException("line must be in [0, 153]");
else
this.scanlinecallback = callback;
}
#endregion
public void Dispose()
{
LibGambatte.gambatte_destroy(GambatteState);

View File

@ -217,12 +217,21 @@
</Compile>
<Compile Include="DisplayManager\DisplayManager.cs" />
<Compile Include="DisplayManager\Filters\Hq2x.cs" />
<Compile Include="GBtools\BmpView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="GBtools\ColorChooserForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GBtools\ColorChooserForm.designer.cs">
<DependentUpon>ColorChooserForm.cs</DependentUpon>
</Compile>
<Compile Include="GBtools\GBGPUView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GBtools\GBGPUView.Designer.cs">
<DependentUpon>GBGPUView.cs</DependentUpon>
</Compile>
<Compile Include="Global.cs" />
<Compile Include="HawkFile.cs" />
<Compile Include="Input\ControllerBinding.cs" />
@ -457,6 +466,9 @@
<EmbeddedResource Include="GBtools\ColorChooserForm.resx">
<DependentUpon>ColorChooserForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="GBtools\GBGPUView.resx">
<DependentUpon>GBGPUView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
<SubType>Designer</SubType>

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace BizHawk.MultiClient.GBtools
{
public partial class BmpView : Control
{
public Bitmap bmp;
public BmpView()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Opaque, true);
this.BackColor = Color.Transparent;
this.Paint += new PaintEventHandler(BmpView_Paint);
this.SizeChanged += new EventHandler(BmpView_SizeChanged);
}
void BmpView_Paint(object sender, PaintEventArgs e)
{
if (bmp != null)
{
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
e.Graphics.DrawImageUnscaled(bmp, 0, 0);
}
}
void BmpView_SizeChanged(object sender, EventArgs e)
{
if (bmp != null)
{
bmp.Dispose();
bmp = null;
}
if (Width == 0 || Height == 0)
return;
bmp = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}
}
}

View File

@ -0,0 +1,98 @@
namespace BizHawk.MultiClient.GBtools
{
partial class GBGPUView
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.bmpViewWin = new BizHawk.MultiClient.GBtools.BmpView();
this.bmpViewBG = new BizHawk.MultiClient.GBtools.BmpView();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(17, 21);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(22, 13);
this.label1.TabIndex = 2;
this.label1.Text = "BG";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(275, 24);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(26, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Win";
//
// bmpViewWin
//
this.bmpViewWin.BackColor = System.Drawing.Color.Transparent;
this.bmpViewWin.Location = new System.Drawing.Point(278, 37);
this.bmpViewWin.Name = "bmpViewWin";
this.bmpViewWin.Size = new System.Drawing.Size(256, 256);
this.bmpViewWin.TabIndex = 5;
this.bmpViewWin.Text = "bmpView2";
//
// bmpViewBG
//
this.bmpViewBG.BackColor = System.Drawing.Color.Transparent;
this.bmpViewBG.Location = new System.Drawing.Point(12, 37);
this.bmpViewBG.Name = "bmpViewBG";
this.bmpViewBG.Size = new System.Drawing.Size(256, 256);
this.bmpViewBG.TabIndex = 4;
this.bmpViewBG.Text = "bmpView1";
//
// GBGPUView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(564, 404);
this.Controls.Add(this.bmpViewWin);
this.Controls.Add(this.bmpViewBG);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Name = "GBGPUView";
this.Text = "GB GPU Viewer";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.GBGPUView_FormClosed);
this.Load += new System.EventHandler(this.GBGPUView_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private BmpView bmpViewBG;
private BmpView bmpViewWin;
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace BizHawk.MultiClient.GBtools
{
public partial class GBGPUView : Form
{
Emulation.Consoles.GB.Gameboy gb;
public GBGPUView()
{
InitializeComponent();
}
public void Restart()
{
if (Global.Emulator is Emulation.Consoles.GB.Gameboy)
{
gb = Global.Emulator as Emulation.Consoles.GB.Gameboy;
}
else
{
this.Close();
}
}
/// <summary>
/// put me in ToolsBefore
/// </summary>
public void UpdateValues()
{
if (!this.IsHandleCreated || this.IsDisposed)
return;
if (gb != null)
if (this.Visible)
gb.SetScanlineCallback(ScanlineCallback, 0);
else
gb.SetScanlineCallback(null, 0);
}
static unsafe void DrawTile(byte* tile, int* dest, int pitch)
{
for (int y = 0; y < 8; y++)
{
int loplane = *tile++;
int hiplane = *tile++;
hiplane <<= 1; // msb
dest += 7;
for (int x = 0; x < 8; x++) // right to left
{
int palcolor = loplane & 1 | hiplane & 2;
// todo: palette transformation
int color = palcolor * 0x555555 | unchecked((int)0xff000000);
*dest-- = color;
loplane >>= 1;
hiplane >>= 1;
}
dest++;
dest += pitch;
}
}
static unsafe void DrawBG(Bitmap b, IntPtr _map, IntPtr _tiles, bool wrap)
{
if (b.Width != 256 || b.Height != 256)
throw new Exception("GPUView screwed up.");
var lockdata = b.LockBits(new Rectangle(0, 0, 256, 256), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte* map = (byte*)_map;
int* dest = (int*)lockdata.Scan0;
int pitch = lockdata.Stride / sizeof(int); // in int*s, not bytes
for (int ty = 0; ty < 32; ty++)
{
for (int tx = 0; tx < 32; tx++)
{
int tileindex = map[0];
if (wrap && tileindex >= 128)
tileindex -= 256;
byte* tile = (byte*)(_tiles + tileindex * 16);
DrawTile(tile, dest, pitch);
map++;
dest += 8;
}
dest -= 256;
dest += pitch * 8;
}
b.UnlockBits(lockdata);
}
/// <summary>
/// core calls this on scanlines
/// </summary>
/// <param name="vram"></param>
/// <param name="vramlength"></param>
/// <param name="lcdc"></param>
void ScanlineCallback(IntPtr vram, int vramlength, int lcdc)
{
DrawBG(
bmpViewBG.bmp,
vram + (lcdc.Bit(3) ? 0x1c00 : 0x1800),
vram + (lcdc.Bit(4) ? 0x0000 : 0x1000),
!lcdc.Bit(4));
bmpViewBG.Refresh();
DrawBG(
bmpViewWin.bmp,
vram + (lcdc.Bit(6) ? 0x1c00 : 0x1800),
vram + 0x1000, // force win to second tile bank???
true);
bmpViewWin.Refresh();
}
private void GBGPUView_FormClosed(object sender, FormClosedEventArgs e)
{
if (gb != null)
{
gb.SetScanlineCallback(null, 0);
gb = null;
}
}
private void GBGPUView_Load(object sender, EventArgs e)
{
gb = Global.Emulator as Emulation.Consoles.GB.Gameboy;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -306,6 +306,7 @@
this.cmiScreenshotClipboard = new System.Windows.Forms.ToolStripMenuItem();
this.cmiCloseRom = new System.Windows.Forms.ToolStripMenuItem();
this.cmiShowMenu = new System.Windows.Forms.ToolStripMenuItem();
this.gPUViewerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip1.SuspendLayout();
this.StatusSlot0.SuspendLayout();
this.contextMenuStrip1.SuspendLayout();
@ -2118,7 +2119,8 @@
this.multicartCompatibilityToolStripMenuItem,
this.toolStripSeparator8,
this.changeDMGPalettesToolStripMenuItem,
this.loadGBInSGBToolStripMenuItem1});
this.loadGBInSGBToolStripMenuItem1,
this.gPUViewerToolStripMenuItem});
this.gBToolStripMenuItem.Name = "gBToolStripMenuItem";
this.gBToolStripMenuItem.Size = new System.Drawing.Size(32, 17);
this.gBToolStripMenuItem.Text = "&GB";
@ -2645,6 +2647,13 @@
this.cmiShowMenu.Text = "Show Menu";
this.cmiShowMenu.Click += new System.EventHandler(this.showMenuToolStripMenuItem_Click);
//
// gPUViewerToolStripMenuItem
//
this.gPUViewerToolStripMenuItem.Name = "gPUViewerToolStripMenuItem";
this.gPUViewerToolStripMenuItem.Size = new System.Drawing.Size(190, 22);
this.gPUViewerToolStripMenuItem.Text = "GPU Viewer";
this.gPUViewerToolStripMenuItem.Click += new System.EventHandler(this.gPUViewerToolStripMenuItem_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 14F);
@ -2962,6 +2971,7 @@
private System.Windows.Forms.ToolStripMenuItem showMissle2ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem showBallToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem showPlayfieldToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem gPUViewerToolStripMenuItem;
}
}

View File

@ -77,6 +77,7 @@ namespace BizHawk.MultiClient
public NESNameTableViewer NESNameTableViewer1 = new NESNameTableViewer();
public NESPPU NESPPU1 = new NESPPU();
public NESDebugger NESDebug1 = new NESDebugger();
public GBtools.GBGPUView GBGPUView1 = new GBtools.GBGPUView();
public PCEBGViewer PCEBGViewer1 = new PCEBGViewer();
public Cheats Cheats1 = new Cheats();
public ToolBox ToolBox1 = new ToolBox();
@ -2497,6 +2498,7 @@ namespace BizHawk.MultiClient
NESNameTableViewer1.UpdateValues();
NESPPU1.UpdateValues();
PCEBGViewer1.UpdateValues();
GBGPUView1.UpdateValues();
}
public void UpdateToolsLoadstate()
@ -2866,6 +2868,17 @@ namespace BizHawk.MultiClient
PCEBGViewer1.Focus();
}
public void LoadGBGPUView()
{
if (!GBGPUView1.IsHandleCreated || GBGPUView1.IsDisposed)
{
GBGPUView1 = new GBtools.GBGPUView();
GBGPUView1.Show();
}
else
GBGPUView1.Focus();
}
public void LoadTI83KeyPad()
{
if (!TI83KeyPad1.IsHandleCreated || TI83KeyPad1.IsDisposed)
@ -3099,6 +3112,7 @@ namespace BizHawk.MultiClient
NESPPU1.Restart();
NESNameTableViewer1.Restart();
NESDebug1.Restart();
GBGPUView1.Restart();
PCEBGViewer1.Restart();
TI83KeyPad1.Restart();
Cheats1.Restart();
@ -3137,6 +3151,7 @@ namespace BizHawk.MultiClient
CloseForm(NESNameTableViewer1);
CloseForm(NESPPU1);
CloseForm(NESDebug1);
CloseForm(GBGPUView1);
CloseForm(PCEBGViewer1);
CloseForm(Cheats1);
CloseForm(TI83KeyPad1);
@ -4249,5 +4264,10 @@ namespace BizHawk.MultiClient
Global.Config.Atari2600_ShowPlayfield ^= true;
SyncCoreInputComm();
}
private void gPUViewerToolStripMenuItem_Click(object sender, EventArgs e)
{
LoadGBGPUView();
}
}
}