gba: add the beginnings of a GPU view. much work is needed, but i like the idea of a single panel with movable widgets (and config-saveable layout, of course), as there is too much information in the case of a GBA to reasonably show it all on a screen. this way the user can set up a "workspace" appropriate for whatever dumping/debugging project is being done.

This commit is contained in:
goyuken 2012-11-27 19:21:46 +00:00
parent 175caf3a53
commit 965c6d8700
8 changed files with 537 additions and 6 deletions

View File

@ -245,6 +245,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo.GBA
_MemoryDomains.Add(sb);
}
public void GetGPUMemoryAreas(out IntPtr vram, out IntPtr palram, out IntPtr oam, out IntPtr mmio)
{
IntPtr _vram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.vram);
IntPtr _palram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.palram);
IntPtr _oam = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.oam);
IntPtr _mmio = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.io);
if (_vram == IntPtr.Zero || _palram == IntPtr.Zero || _oam == IntPtr.Zero || _mmio == IntPtr.Zero)
throw new Exception("libmeteor_getmemoryarea() failed!");
vram = _vram;
palram = _palram;
oam = _oam;
mmio = _mmio;
}
#endregion
/// <summary>like libsnes, the library is single-instance</summary>

View File

@ -118,6 +118,10 @@ namespace BizHawk
return (b & (1 << index)) != 0;
}
public static bool Bit(this ushort b, int index)
{
return (b & (1 << index)) != 0;
}
public static string GetPrecedingString(this string str, string value)
{

View File

@ -226,6 +226,12 @@
<Compile Include="GBAtools\GBAGPUView.Designer.cs">
<DependentUpon>GBAGPUView.cs</DependentUpon>
</Compile>
<Compile Include="GBAtools\MobileBmpView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GBAtools\MobileBmpView.Designer.cs">
<DependentUpon>MobileBmpView.cs</DependentUpon>
</Compile>
<Compile Include="GBtools\BmpView.cs">
<SubType>Component</SubType>
</Compile>
@ -500,6 +506,9 @@
<EmbeddedResource Include="GBAtools\GBAGPUView.resx">
<DependentUpon>GBAGPUView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="GBAtools\MobileBmpView.resx">
<DependentUpon>MobileBmpView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="GBtools\CGBColorChooserForm.resx">
<DependentUpon>CGBColorChooserForm.cs</DependentUpon>
</EmbeddedResource>

View File

@ -28,26 +28,63 @@
/// </summary>
private void InitializeComponent()
{
this.listBoxWidgets = new System.Windows.Forms.ListBox();
this.panel1 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label();
this.buttonShowWidget = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// listBoxWidgets
//
this.listBoxWidgets.Location = new System.Drawing.Point(12, 25);
this.listBoxWidgets.Name = "listBoxWidgets";
this.listBoxWidgets.Size = new System.Drawing.Size(120, 160);
this.listBoxWidgets.TabIndex = 0;
this.listBoxWidgets.DoubleClick += new System.EventHandler(this.listBoxWidgets_DoubleClick);
//
// panel1
//
this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel1.Location = new System.Drawing.Point(138, 12);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(486, 381);
this.panel1.TabIndex = 1;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(42, 21);
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(115, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Doesn\'t exist yet, sorry!";
this.label1.Size = new System.Drawing.Size(92, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Available widgets:";
//
// buttonShowWidget
//
this.buttonShowWidget.Location = new System.Drawing.Point(29, 191);
this.buttonShowWidget.Name = "buttonShowWidget";
this.buttonShowWidget.Size = new System.Drawing.Size(75, 23);
this.buttonShowWidget.TabIndex = 3;
this.buttonShowWidget.Text = "Show >>";
this.buttonShowWidget.UseVisualStyleBackColor = true;
this.buttonShowWidget.Click += new System.EventHandler(this.buttonShowWidget_Click);
//
// GBAGPUView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.ClientSize = new System.Drawing.Size(636, 405);
this.Controls.Add(this.buttonShowWidget);
this.Controls.Add(this.label1);
this.Controls.Add(this.panel1);
this.Controls.Add(this.listBoxWidgets);
this.Name = "GBAGPUView";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
this.Text = "GBA GPU Viewer";
this.Load += new System.EventHandler(this.GBAGPUView_Load);
this.ResumeLayout(false);
this.PerformLayout();
@ -55,6 +92,10 @@
#endregion
private System.Windows.Forms.ListBox listBoxWidgets;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button buttonShowWidget;
}
}

View File

@ -6,20 +6,204 @@ using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BizHawk.MultiClient.GBtools;
namespace BizHawk.MultiClient.GBAtools
{
public partial class GBAGPUView : Form
{
Emulation.Consoles.Nintendo.GBA.GBA gba;
// emulator memory areas
IntPtr vram;
IntPtr oam;
IntPtr mmio;
IntPtr palram;
// color conversion to RGB888
int[] ColorConversion;
MobileBmpView bg0, bg1, bg2, bg3;
public GBAGPUView()
{
InitializeComponent();
// TODO: hook up something
// we do this twice to avoid having to & 0x7fff with every color
int[] tmp = Emulation.Consoles.GB.GBColors.GetLut(Emulation.Consoles.GB.GBColors.ColorType.vivid);
ColorConversion = new int[65536];
Buffer.BlockCopy(tmp, 0, ColorConversion, 0, sizeof(int) * tmp.Length);
Buffer.BlockCopy(tmp, 0, ColorConversion, sizeof(int) * tmp.Length, sizeof(int) * tmp.Length);
/*
for (int i = 1; i <= 2; i++)
{
Form foobar = new Form();
foobar.Text = "foo" + i;
foobar.TopLevel = false;
//foobar.TopLevelControl = this;
foobar.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
//foobar.SetBounds(10, 10, 100, 100);
foobar.Location = new Point(100, 100);
foobar.Size = new System.Drawing.Size(100, 100);
//foobar.BackColor = Color.Black;
Controls.Add(foobar);
int j = i;
//foobar.Activated += (o, e) => MessageBox.Show("activated" + j);
//foobar.Enter += (o, e) => MessageBox.Show("enter" + j);
//foobar.Leave += (o, e) => MessageBox.Show("leave" + j);
foobar.MouseEnter += (o, e) => foobar.Activate();
foobar.Controls.Add(new Button());
foobar.Show();
}
*/
GenerateWidgets();
}
#region drawing primitives
unsafe void DrawTile16(int* dest, int pitch, byte* tile, ushort *palette, bool hflip, bool vflip)
{
if (vflip)
{
dest += pitch * 7;
pitch = -pitch;
}
for (int y = 0; y < 8; y++)
{
for (int i = 0; i < 4; i++)
{
*dest++ = ColorConversion[palette[*tile & 15]];
*dest++ = ColorConversion[palette[*tile >> 4]];
tile++;
}
dest -= 8;
dest += pitch;
}
}
unsafe void DrawTextNameTable16(int* dest, int pitch, ushort* nametable, byte* tiles)
{
for (int ty = 0; ty < 32; ty++)
{
for (int tx = 0; tx < 32; tx++)
{
ushort ntent = *nametable++;
DrawTile16(dest, pitch, tiles + (ntent & 1023) * 32, (ushort*)palram + (ntent >> 12 << 4), ntent.Bit(10), ntent.Bit(11));
dest += 8;
}
dest -= 256;
dest += 8 * pitch;
}
}
unsafe void DrawTextNameTable(int* dest, int pitch, ushort* nametable, byte* tiles, bool eightbit)
{
if (eightbit)
;
else
DrawTextNameTable16(dest, pitch, nametable, tiles);
}
unsafe void DrawTextBG(int n, MobileBmpView mbv)
{
ushort bgcnt = ((ushort*)mmio)[4 + n];
int ssize = bgcnt >> 14;
switch (ssize)
{
case 0: mbv.ChangeAllSizes(256, 256); break;
case 1: mbv.ChangeAllSizes(512, 256); break;
case 2: mbv.ChangeAllSizes(256, 512); break;
case 3: mbv.ChangeAllSizes(512, 512); break;
}
Bitmap bmp = mbv.bmpView.bmp;
var lockdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int* pixels = (int*)lockdata.Scan0;
int pitch = lockdata.Stride / sizeof(int);
byte* tiles = (byte*)vram + ((bgcnt & 0xc) << 12);
ushort *nametable = (ushort*)vram + ((bgcnt & 0x1f00) << 2);
bool eightbit = bgcnt.Bit(7);
switch (ssize)
{
case 0:
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
break;
case 1:
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
pixels += 256;
nametable += 1024;
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
break;
case 2:
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
pixels += pitch * 256;
nametable += 1024;
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
break;
case 3:
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
pixels += 256;
nametable += 1024;
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
pixels -= 256;
pixels += pitch * 256;
nametable += 1024;
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
pixels += 256;
nametable += 1024;
DrawTextNameTable(pixels, pitch, nametable, tiles, eightbit);
break;
}
bmp.UnlockBits(lockdata);
mbv.bmpView.Refresh();
}
#endregion
MobileBmpView MakeWidget(string text, int w, int h)
{
var mbv = new MobileBmpView();
mbv.Text = text;
mbv.TopLevel = false;
mbv.ChangeViewSize(w, h);
mbv.bmpView.Clear();
mbv.FormClosing += delegate(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
listBoxWidgets.Items.Add(sender);
(sender as Form).Hide();
};
panel1.Controls.Add(mbv);
listBoxWidgets.Items.Add(mbv);
return mbv;
}
void GenerateWidgets()
{
listBoxWidgets.BeginUpdate();
bg0 = MakeWidget("Background 0", 256, 256);
bg1 = MakeWidget("Background 1", 256, 256);
bg2 = MakeWidget("Background 2", 256, 256);
bg3 = MakeWidget("Background 3", 256, 256);
listBoxWidgets.EndUpdate();
}
public void Restart()
{
if (Global.Emulator is Emulation.Consoles.Nintendo.GBA.GBA)
gba = Global.Emulator as Emulation.Consoles.Nintendo.GBA.GBA;
if (gba != null)
{
gba.GetGPUMemoryAreas(out vram, out palram, out oam, out mmio);
}
else
{
@ -28,9 +212,56 @@ namespace BizHawk.MultiClient.GBAtools
}
}
unsafe void DrawEverything()
{
ushort dispcnt = ((ushort*)mmio)[0];
int bgmode = dispcnt & 7;
switch (bgmode)
{
case 0:
if (bg0.ShouldDraw) DrawTextBG(0, bg0);
if (bg1.ShouldDraw) DrawTextBG(1, bg1);
if (bg2.ShouldDraw) DrawTextBG(2, bg2);
if (bg3.ShouldDraw) DrawTextBG(3, bg3);
break;
}
}
/// <summary>belongs in ToolsBefore</summary>
public void UpdateValues()
{
if (!this.IsHandleCreated || this.IsDisposed)
return;
if (gba != null)
{
DrawEverything();
}
}
private void GBAGPUView_Load(object sender, EventArgs e)
{
Restart();
}
void ShowSelectedWidget()
{
if (listBoxWidgets.SelectedItem != null)
{
(listBoxWidgets.SelectedItem as MobileBmpView).Show();
listBoxWidgets.Items.RemoveAt(listBoxWidgets.SelectedIndex);
}
}
private void buttonShowWidget_Click(object sender, EventArgs e)
{
ShowSelectedWidget();
}
private void listBoxWidgets_DoubleClick(object sender, EventArgs e)
{
ShowSelectedWidget();
}
}
}

View File

@ -0,0 +1,61 @@
namespace BizHawk.MultiClient.GBAtools
{
partial class MobileBmpView
{
/// <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.bmpView1 = new BizHawk.MultiClient.GBtools.BmpView();
this.SuspendLayout();
//
// bmpView1
//
this.bmpView1.Location = new System.Drawing.Point(0, 0);
this.bmpView1.Name = "bmpView1";
this.bmpView1.Size = new System.Drawing.Size(64, 64);
this.bmpView1.TabIndex = 0;
this.bmpView1.Text = "bmpView1";
//
// MobileBmpView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.bmpView1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "MobileBmpView";
this.Text = "MobileBmpView";
this.ResumeLayout(false);
}
#endregion
private GBtools.BmpView bmpView1;
}
}

View File

@ -0,0 +1,49 @@
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.GBAtools
{
public partial class MobileBmpView : Form
{
public MobileBmpView()
{
InitializeComponent();
}
public GBtools.BmpView bmpView { get { return bmpView1; } }
[Browsable(false)]
public bool ShouldDraw { get { return this.Visible; } }
public override string ToString()
{
return Text;
}
public void ChangeViewSize(Size size)
{
bmpView1.Size = size;
this.ClientSize = size;
}
public void ChangeViewSize(int w, int h)
{
ChangeViewSize(new Size(w, h));
}
public void ChangeAllSizes(int w, int h)
{
ChangeViewSize(w, h);
bmpView1.ChangeBitmapSize(w, h);
}
public void ChangeAllSizes(Size size)
{
ChangeViewSize(size);
bmpView1.ChangeBitmapSize(size);
}
}
}

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>