diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index fb83d6d8..13b2caa5 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -361,6 +361,7 @@ Create Create + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 6b9178ba..0d581125 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -301,5 +301,8 @@ Source Files\APU + + Source Files + \ No newline at end of file diff --git a/Core/EmulationSettings.cpp b/Core/EmulationSettings.cpp index 75c0511e..6f7e9520 100644 --- a/Core/EmulationSettings.cpp +++ b/Core/EmulationSettings.cpp @@ -4,4 +4,5 @@ uint32_t EmulationSettings::Flags = 0; uint32_t EmulationSettings::AudioLatency = 20000; double EmulationSettings::ChannelVolume[5] = { 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }; -NesModel EmulationSettings::Model; \ No newline at end of file +NesModel EmulationSettings::Model; +OverscanDimensions EmulationSettings::Overscan; \ No newline at end of file diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 58b14523..03921515 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -25,6 +25,29 @@ enum class NesModel PAL = 2, }; +struct OverscanDimensions +{ + uint32_t Left = 0; + uint32_t Right = 0; + uint32_t Top = 0; + uint32_t Bottom = 0; + + uint32_t GetPixelCount() + { + return GetScreenWidth() * GetScreenHeight(); + } + + uint32_t GetScreenWidth() + { + return 256 - Left - Right; + } + + uint32_t GetScreenHeight() + { + return 240 - Top - Bottom; + } +}; + class EmulationSettings { private: @@ -32,6 +55,7 @@ private: static uint32_t AudioLatency; static double ChannelVolume[5]; static NesModel Model; + static OverscanDimensions Overscan; public: static void SetFlags(uint32_t flags) @@ -70,6 +94,19 @@ public: AudioLatency = msLatency; } + static void SetOverscanDimensions(uint8_t left, uint8_t right, uint8_t top, uint8_t bottom) + { + Overscan.Left = left; + Overscan.Right = right; + Overscan.Top = top; + Overscan.Bottom = bottom; + } + + static OverscanDimensions GetOverscanDimensions() + { + return Overscan; + } + static double GetChannelVolume(AudioChannel channel) { return ChannelVolume[(int)channel]; diff --git a/Core/PPU.h b/Core/PPU.h index c4aadebc..553b2b13 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -182,6 +182,11 @@ class PPU : public IMemoryHandler, public Snapshotable void StreamState(bool saving); public: + static const uint32_t ScreenWidth = 256; + static const uint32_t ScreenHeight = 240; + static const uint32_t PixelCount = 256*240; + static const uint32_t OutputBufferSize = 256*240*2; + PPU(MemoryManager *memoryManager); ~PPU(); diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp new file mode 100644 index 00000000..9589aa63 --- /dev/null +++ b/Core/VideoDecoder.cpp @@ -0,0 +1,143 @@ +#pragma once +#include "stdafx.h" +#include "VideoDecoder.h" +#include "EmulationSettings.h" +#include "MessageManager.h" +#include "../Utilities/PNGWriter.h" +#include "../Utilities/FolderUtilities.h" + +const uint32_t PPU_PALETTE_ARGB[] = { + 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, + 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, + 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, + 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, + 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, + 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, + 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, + 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, + 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, + 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, + 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, + 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, + 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000, +}; + +unique_ptr VideoDecoder::Instance; + +VideoDecoder* VideoDecoder::GetInstance() +{ + if(!Instance) { + Instance.reset(new VideoDecoder()); + } + return Instance.get(); +} + +VideoDecoder::~VideoDecoder() +{ + if(_frameBuffer) { + delete[] _frameBuffer; + } +} + +uint32_t VideoDecoder::ProcessIntensifyBits(uint16_t ppuPixel) +{ + uint32_t pixelOutput = PPU_PALETTE_ARGB[ppuPixel & 0x3F]; + + //Incorrect emphasis bit implementation, but will do for now. + float redChannel = (float)((pixelOutput & 0xFF0000) >> 16); + float greenChannel = (float)((pixelOutput & 0xFF00) >> 8); + float blueChannel = (float)(pixelOutput & 0xFF); + + if(ppuPixel & 0x40) { + //Intensify red + redChannel *= 1.1f; + greenChannel *= 0.9f; + blueChannel *= 0.9f; + } + if(ppuPixel & 0x80) { + //Intensify green + greenChannel *= 1.1f; + redChannel *= 0.9f; + blueChannel *= 0.9f; + } + if(ppuPixel & 0x100) { + //Intensify blue + blueChannel *= 1.1f; + redChannel *= 0.9f; + greenChannel *= 0.9f; + } + + uint8_t r, g, b; + r = (uint8_t)fmin(redChannel, 255); + g = (uint8_t)fmin(greenChannel, 255); + b = (uint8_t)fmin(blueChannel, 255); + + return 0xFF000000 | (r << 16) | (g << 8) | b; +} + +void VideoDecoder::UpdateBufferSize() +{ + OverscanDimensions overscan = EmulationSettings::GetOverscanDimensions(); + + if(!_frameBuffer || _overscan.GetPixelCount() != overscan.GetPixelCount()) { + _overscan = overscan; + if(_frameBuffer) { + delete[] _frameBuffer; + } + _frameBuffer = new uint32_t[_overscan.GetPixelCount()]; + } +} + +uint32_t* VideoDecoder::DecodeFrame(uint16_t* inputBuffer) +{ + _frameLock.Acquire(); + UpdateBufferSize(); + uint32_t* outputBuffer = _frameBuffer; + for(uint32_t i = _overscan.Top, iMax = 240 - _overscan.Bottom; i < iMax; i++) { + for(uint32_t j = _overscan.Left, jMax = 256 - _overscan.Right; j < jMax; j++) { + *outputBuffer = ProcessIntensifyBits(inputBuffer[i*256+j]); + outputBuffer++; + } + } + _frameLock.Release(); + + return _frameBuffer; +} + +void VideoDecoder::TakeScreenshot(string romFilename) +{ + uint32_t bufferSize = _overscan.GetPixelCount() * 4; + uint32_t* frameBuffer = new uint32_t[bufferSize]; + + _frameLock.Acquire(); + memcpy(frameBuffer, _frameBuffer, bufferSize); + _frameLock.Release(); + + //ARGB -> ABGR + for(uint32_t i = 0; i < bufferSize; i++) { + frameBuffer[i] = (frameBuffer[i] & 0xFF00FF00) | ((frameBuffer[i] & 0xFF0000) >> 16) | ((frameBuffer[i] & 0xFF) << 16); + } + + int counter = 0; + string baseFilename = FolderUtilities::GetScreenshotFolder() + romFilename; + string ssFilename; + while(true) { + string counterStr = std::to_string(counter); + while(counterStr.length() < 3) { + counterStr = "0" + counterStr; + } + ssFilename = baseFilename + "_" + counterStr + ".png"; + ifstream file(ssFilename, ios::in); + if(file) { + file.close(); + } else { + break; + } + counter++; + } + + PNGWriter::WritePNG(ssFilename, (uint8_t*)frameBuffer, _overscan.GetScreenWidth(), _overscan.GetScreenHeight()); + delete[] frameBuffer; + + MessageManager::DisplayMessage("Screenshot saved", FolderUtilities::GetFilename(ssFilename, true)); +} \ No newline at end of file diff --git a/Core/VideoDecoder.h b/Core/VideoDecoder.h index 8fbc7a1f..b068f317 100644 --- a/Core/VideoDecoder.h +++ b/Core/VideoDecoder.h @@ -1,67 +1,24 @@ #pragma once #include "stdafx.h" -#include - -const uint32_t PPU_PALETTE_ARGB[] = { - 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, - 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, - 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, - 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, - 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, - 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, - 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, - 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, - 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, - 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, - 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, - 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, - 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000, -}; +#include "../Utilities/SimpleLock.h" +#include "EmulationSettings.h" class VideoDecoder { private: - static uint32_t ProcessIntensifyBits(uint16_t ppuPixel) - { - uint32_t pixelOutput = PPU_PALETTE_ARGB[ppuPixel & 0x3F]; + static unique_ptr Instance; - //Incorrect emphasis bit implementation, but will do for now. - float redChannel = (float)((pixelOutput & 0xFF0000) >> 16); - float greenChannel = (float)((pixelOutput & 0xFF00) >> 8); - float blueChannel = (float)(pixelOutput & 0xFF); + OverscanDimensions _overscan; + uint32_t* _frameBuffer = nullptr; + SimpleLock _frameLock; - if(ppuPixel & 0x40) { - //Intensify red - redChannel *= 1.1f; - greenChannel *= 0.9f; - blueChannel *= 0.9f; - } - if(ppuPixel & 0x80) { - //Intensify green - greenChannel *= 1.1f; - redChannel *= 0.9f; - blueChannel *= 0.9f; - } - if(ppuPixel & 0x100) { - //Intensify blue - blueChannel *= 1.1f; - redChannel *= 0.9f; - greenChannel *= 0.9f; - } - - uint8_t r, g, b; - r = (uint8_t)fmin(redChannel, 255); - g = (uint8_t)fmin(greenChannel, 255); - b = (uint8_t)fmin(blueChannel, 255); - - return 0xFF000000 | (r << 16) | (g << 8) | b; - } + uint32_t ProcessIntensifyBits(uint16_t ppuPixel); + void UpdateBufferSize(); public: - static void DecodeFrame(uint16_t* inputBuffer, uint32_t* outputBuffer) - { - for(uint32_t i = 0; i < 256*240; i++) { - ((uint32_t*)outputBuffer)[i] = ProcessIntensifyBits(inputBuffer[i]); - } - } + static VideoDecoder* GetInstance(); + ~VideoDecoder(); + + uint32_t* DecodeFrame(uint16_t* inputBuffer); + void TakeScreenshot(string romFilename); }; \ No newline at end of file diff --git a/GUI.NET/Config/VideoInfo.cs b/GUI.NET/Config/VideoInfo.cs index 4093b5ad..69acd598 100644 --- a/GUI.NET/Config/VideoInfo.cs +++ b/GUI.NET/Config/VideoInfo.cs @@ -13,6 +13,10 @@ namespace Mesen.GUI.Config { public bool LimitFPS = true; public bool ShowFPS = false; + public UInt32 OverscanLeft = 0; + public UInt32 OverscanRight = 0; + public UInt32 OverscanTop = 8; + public UInt32 OverscanBottom = 8; public VideoInfo() { @@ -20,6 +24,7 @@ namespace Mesen.GUI.Config static public void ApplyConfig() { + VideoInfo videoInfo = ConfigManager.Config.VideoInfo; if(ConfigManager.Config.VideoInfo.LimitFPS) { InteropEmu.SetFlags((UInt32)EmulationFlags.LimitFPS); } else { @@ -31,6 +36,14 @@ namespace Mesen.GUI.Config } else { InteropEmu.ClearFlags((UInt32)EmulationFlags.ShowFPS); } + + InteropEmu.SetOverscanDimensions(videoInfo.OverscanLeft, videoInfo.OverscanRight, videoInfo.OverscanTop, videoInfo.OverscanBottom); + } + + static public Size GetViewerSize() + { + VideoInfo videoInfo = ConfigManager.Config.VideoInfo; + return new Size((int)(256-videoInfo.OverscanLeft-videoInfo.OverscanRight)*4, (int)(240-videoInfo.OverscanTop-videoInfo.OverscanBottom)*4); } } } diff --git a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs index d7d7ea32..e64f7c38 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs @@ -27,107 +27,291 @@ /// private void InitializeComponent() { + this.tlpMain = new System.Windows.Forms.TableLayoutPanel(); + this.grpCropping = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.txtPort = new System.Windows.Forms.TextBox(); - this.lblHost = new System.Windows.Forms.Label(); - this.lblPort = new System.Windows.Forms.Label(); - this.txtHost = new System.Windows.Forms.TextBox(); - this.chkSpectator = new System.Windows.Forms.CheckBox(); + this.picOverscan = new System.Windows.Forms.PictureBox(); + this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblLeft = new System.Windows.Forms.Label(); + this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblTop = new System.Windows.Forms.Label(); + this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblBottom = new System.Windows.Forms.Label(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblRight = new System.Windows.Forms.Label(); + this.chkLimitFps = new System.Windows.Forms.CheckBox(); + this.chkShowFps = new System.Windows.Forms.CheckBox(); + this.nudOverscanRight = new System.Windows.Forms.NumericUpDown(); + this.nudOverscanBottom = new System.Windows.Forms.NumericUpDown(); + this.nudOverscanLeft = new System.Windows.Forms.NumericUpDown(); + this.nudOverscanTop = new System.Windows.Forms.NumericUpDown(); + this.tlpMain.SuspendLayout(); + this.grpCropping.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picOverscan)).BeginInit(); + this.flowLayoutPanel3.SuspendLayout(); + this.flowLayoutPanel4.SuspendLayout(); + this.flowLayoutPanel5.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanRight)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanBottom)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanLeft)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanTop)).BeginInit(); this.SuspendLayout(); // + // baseConfigPanel + // + this.baseConfigPanel.Location = new System.Drawing.Point(0, 308); + this.baseConfigPanel.Size = new System.Drawing.Size(362, 29); + // + // tlpMain + // + this.tlpMain.ColumnCount = 1; + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tlpMain.Controls.Add(this.grpCropping, 0, 2); + this.tlpMain.Controls.Add(this.chkLimitFps, 0, 0); + this.tlpMain.Controls.Add(this.chkShowFps, 0, 1); + this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpMain.Location = new System.Drawing.Point(0, 0); + this.tlpMain.Name = "tlpMain"; + this.tlpMain.RowCount = 3; + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpMain.Size = new System.Drawing.Size(362, 308); + this.tlpMain.TabIndex = 1; + // + // grpCropping + // + this.grpCropping.Controls.Add(this.tableLayoutPanel1); + this.grpCropping.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpCropping.Location = new System.Drawing.Point(3, 49); + this.grpCropping.Name = "grpCropping"; + this.grpCropping.Size = new System.Drawing.Size(356, 256); + this.grpCropping.TabIndex = 7; + this.grpCropping.TabStop = false; + this.grpCropping.Text = "Video Cropping"; + // // tableLayoutPanel1 // - this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnCount = 3; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.Controls.Add(this.txtPort, 1, 1); - this.tableLayoutPanel1.Controls.Add(this.lblHost, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.lblPort, 0, 1); - this.tableLayoutPanel1.Controls.Add(this.txtHost, 1, 0); - this.tableLayoutPanel1.Controls.Add(this.chkSpectator, 0, 2); - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.picOverscan, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel3, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel4, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel5, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel2, 2, 1); + this.tableLayoutPanel1.Location = new System.Drawing.Point(9, 19); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 4; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowCount = 3; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(284, 175); - this.tableLayoutPanel1.TabIndex = 1; + this.tableLayoutPanel1.Size = new System.Drawing.Size(277, 234); + this.tableLayoutPanel1.TabIndex = 0; // - // txtPort + // picOverscan // - this.txtPort.Dock = System.Windows.Forms.DockStyle.Fill; - this.txtPort.Location = new System.Drawing.Point(41, 29); - this.txtPort.Name = "txtPort"; - this.txtPort.Size = new System.Drawing.Size(240, 20); - this.txtPort.TabIndex = 6; - this.txtPort.Text = "8888"; + this.picOverscan.Dock = System.Windows.Forms.DockStyle.Fill; + this.picOverscan.Location = new System.Drawing.Point(56, 43); + this.picOverscan.Name = "picOverscan"; + this.picOverscan.Size = new System.Drawing.Size(165, 148); + this.picOverscan.TabIndex = 1; + this.picOverscan.TabStop = false; // - // lblHost + // flowLayoutPanel3 // - this.lblHost.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblHost.AutoSize = true; - this.lblHost.Location = new System.Drawing.Point(3, 6); - this.lblHost.Name = "lblHost"; - this.lblHost.Size = new System.Drawing.Size(32, 13); - this.lblHost.TabIndex = 3; - this.lblHost.Text = "Host:"; + this.flowLayoutPanel3.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.flowLayoutPanel3.Controls.Add(this.lblLeft); + this.flowLayoutPanel3.Controls.Add(this.nudOverscanLeft); + this.flowLayoutPanel3.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 97); + this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel3.Name = "flowLayoutPanel3"; + this.flowLayoutPanel3.Size = new System.Drawing.Size(53, 40); + this.flowLayoutPanel3.TabIndex = 1; // - // lblPort + // lblLeft // - this.lblPort.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblPort.AutoSize = true; - this.lblPort.Location = new System.Drawing.Point(3, 32); - this.lblPort.Name = "lblPort"; - this.lblPort.Size = new System.Drawing.Size(29, 13); - this.lblPort.TabIndex = 4; - this.lblPort.Text = "Port:"; + this.lblLeft.AutoSize = true; + this.lblLeft.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblLeft.Location = new System.Drawing.Point(3, 0); + this.lblLeft.Name = "lblLeft"; + this.lblLeft.Size = new System.Drawing.Size(50, 13); + this.lblLeft.TabIndex = 0; + this.lblLeft.Text = "Left"; + this.lblLeft.TextAlign = System.Drawing.ContentAlignment.TopCenter; // - // txtHost + // flowLayoutPanel4 // - this.txtHost.Dock = System.Windows.Forms.DockStyle.Fill; - this.txtHost.Location = new System.Drawing.Point(41, 3); - this.txtHost.Name = "txtHost"; - this.txtHost.Size = new System.Drawing.Size(240, 20); - this.txtHost.TabIndex = 5; - this.txtHost.Text = "127.0.0.1"; + this.flowLayoutPanel4.Anchor = System.Windows.Forms.AnchorStyles.None; + this.flowLayoutPanel4.Controls.Add(this.lblTop); + this.flowLayoutPanel4.Controls.Add(this.nudOverscanTop); + this.flowLayoutPanel4.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel4.Location = new System.Drawing.Point(112, 0); + this.flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel4.Name = "flowLayoutPanel4"; + this.flowLayoutPanel4.Size = new System.Drawing.Size(53, 40); + this.flowLayoutPanel4.TabIndex = 2; // - // chkSpectator + // lblTop // - this.chkSpectator.AutoSize = true; - this.tableLayoutPanel1.SetColumnSpan(this.chkSpectator, 2); - this.chkSpectator.Enabled = false; - this.chkSpectator.Location = new System.Drawing.Point(3, 55); - this.chkSpectator.Name = "chkSpectator"; - this.chkSpectator.Size = new System.Drawing.Size(106, 17); - this.chkSpectator.TabIndex = 7; - this.chkSpectator.Text = "Join as spectator"; - this.chkSpectator.UseVisualStyleBackColor = true; + this.lblTop.AutoSize = true; + this.lblTop.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblTop.Location = new System.Drawing.Point(3, 0); + this.lblTop.Name = "lblTop"; + this.lblTop.Size = new System.Drawing.Size(50, 13); + this.lblTop.TabIndex = 0; + this.lblTop.Text = "Top"; + this.lblTop.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // flowLayoutPanel5 + // + this.flowLayoutPanel5.Anchor = System.Windows.Forms.AnchorStyles.None; + this.flowLayoutPanel5.Controls.Add(this.lblBottom); + this.flowLayoutPanel5.Controls.Add(this.nudOverscanBottom); + this.flowLayoutPanel5.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel5.Location = new System.Drawing.Point(112, 194); + this.flowLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel5.Name = "flowLayoutPanel5"; + this.flowLayoutPanel5.Size = new System.Drawing.Size(53, 40); + this.flowLayoutPanel5.TabIndex = 3; + // + // lblBottom + // + this.lblBottom.AutoSize = true; + this.lblBottom.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblBottom.Location = new System.Drawing.Point(3, 0); + this.lblBottom.Name = "lblBottom"; + this.lblBottom.Size = new System.Drawing.Size(50, 13); + this.lblBottom.TabIndex = 0; + this.lblBottom.Text = "Bottom"; + this.lblBottom.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.flowLayoutPanel2.Controls.Add(this.lblRight); + this.flowLayoutPanel2.Controls.Add(this.nudOverscanRight); + this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel2.Location = new System.Drawing.Point(224, 97); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(53, 40); + this.flowLayoutPanel2.TabIndex = 0; + // + // lblRight + // + this.lblRight.AutoSize = true; + this.lblRight.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblRight.Location = new System.Drawing.Point(3, 0); + this.lblRight.Name = "lblRight"; + this.lblRight.Size = new System.Drawing.Size(50, 13); + this.lblRight.TabIndex = 0; + this.lblRight.Text = "Right"; + this.lblRight.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // chkLimitFps + // + this.chkLimitFps.AutoSize = true; + this.chkLimitFps.Location = new System.Drawing.Point(3, 3); + this.chkLimitFps.Name = "chkLimitFps"; + this.chkLimitFps.Size = new System.Drawing.Size(70, 17); + this.chkLimitFps.TabIndex = 8; + this.chkLimitFps.Text = "Limit FPS"; + this.chkLimitFps.UseVisualStyleBackColor = true; + // + // chkShowFps + // + this.chkShowFps.AutoSize = true; + this.chkShowFps.Location = new System.Drawing.Point(3, 26); + this.chkShowFps.Name = "chkShowFps"; + this.chkShowFps.Size = new System.Drawing.Size(76, 17); + this.chkShowFps.TabIndex = 9; + this.chkShowFps.Text = "Show FPS"; + this.chkShowFps.UseVisualStyleBackColor = true; + // + // nudRight + // + this.nudOverscanRight.Location = new System.Drawing.Point(3, 16); + this.nudOverscanRight.Name = "nudRight"; + this.nudOverscanRight.Size = new System.Drawing.Size(50, 20); + this.nudOverscanRight.TabIndex = 1; + // + // nudBottom + // + this.nudOverscanBottom.Location = new System.Drawing.Point(3, 16); + this.nudOverscanBottom.Name = "nudBottom"; + this.nudOverscanBottom.Size = new System.Drawing.Size(50, 20); + this.nudOverscanBottom.TabIndex = 2; + // + // nudLeft + // + this.nudOverscanLeft.Location = new System.Drawing.Point(3, 16); + this.nudOverscanLeft.Name = "nudLeft"; + this.nudOverscanLeft.Size = new System.Drawing.Size(50, 20); + this.nudOverscanLeft.TabIndex = 2; + // + // nudTop + // + this.nudOverscanTop.Location = new System.Drawing.Point(3, 16); + this.nudOverscanTop.Name = "nudTop"; + this.nudOverscanTop.Size = new System.Drawing.Size(50, 20); + this.nudOverscanTop.TabIndex = 2; // // frmVideoConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(284, 262); - this.Controls.Add(this.tableLayoutPanel1); + this.ClientSize = new System.Drawing.Size(362, 337); + this.Controls.Add(this.tlpMain); this.Name = "frmVideoConfig"; this.Text = "Video Options"; - this.Controls.SetChildIndex(this.tableLayoutPanel1, 0); + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.Controls.SetChildIndex(this.tlpMain, 0); + this.tlpMain.ResumeLayout(false); + this.tlpMain.PerformLayout(); + this.grpCropping.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picOverscan)).EndInit(); + this.flowLayoutPanel3.ResumeLayout(false); + this.flowLayoutPanel3.PerformLayout(); + this.flowLayoutPanel4.ResumeLayout(false); + this.flowLayoutPanel4.PerformLayout(); + this.flowLayoutPanel5.ResumeLayout(false); + this.flowLayoutPanel5.PerformLayout(); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanRight)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanBottom)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanLeft)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudOverscanTop)).EndInit(); this.ResumeLayout(false); } #endregion + private System.Windows.Forms.TableLayoutPanel tlpMain; + private System.Windows.Forms.GroupBox grpCropping; + private System.Windows.Forms.CheckBox chkLimitFps; + private System.Windows.Forms.CheckBox chkShowFps; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - private System.Windows.Forms.TextBox txtPort; - private System.Windows.Forms.Label lblHost; - private System.Windows.Forms.Label lblPort; - private System.Windows.Forms.TextBox txtHost; - private System.Windows.Forms.CheckBox chkSpectator; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; + private System.Windows.Forms.Label lblLeft; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; + private System.Windows.Forms.Label lblTop; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5; + private System.Windows.Forms.Label lblBottom; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Label lblRight; + private System.Windows.Forms.PictureBox picOverscan; + private System.Windows.Forms.NumericUpDown nudOverscanLeft; + private System.Windows.Forms.NumericUpDown nudOverscanTop; + private System.Windows.Forms.NumericUpDown nudOverscanBottom; + private System.Windows.Forms.NumericUpDown nudOverscanRight; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmVideoConfig.cs b/GUI.NET/Forms/Config/frmVideoConfig.cs index c1f7f20a..3ba7684a 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using Mesen.GUI.Config; namespace Mesen.GUI.Forms.Config { @@ -15,6 +16,21 @@ namespace Mesen.GUI.Forms.Config public frmVideoConfig() { InitializeComponent(); + + Entity = ConfigManager.Config.VideoInfo; + + AddBinding("ShowFPS", chkShowFps); + AddBinding("LimitFPS", chkLimitFps); + AddBinding("OverscanLeft", nudOverscanLeft); + AddBinding("OverscanRight", nudOverscanRight); + AddBinding("OverscanTop", nudOverscanTop); + AddBinding("OverscanBottom", nudOverscanBottom); + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + base.OnFormClosed(e); + VideoInfo.ApplyConfig(); } } } diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index d6e79ce9..cda20401 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -51,9 +51,13 @@ this.mnuLimitFPS = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowFPS = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuInput = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuVideoConfig = new System.Windows.Forms.ToolStripMenuItem(); this.mnuAudioConfig = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInput = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegion = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionAuto = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionNtsc = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionPal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoConfig = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTools = new System.Windows.Forms.ToolStripMenuItem(); this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem(); this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem(); @@ -78,10 +82,6 @@ this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); this.dxViewer = new Mesen.GUI.Controls.DXViewer(); - this.mnuRegion = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionAuto = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionNtsc = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionPal = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip.SuspendLayout(); this.SuspendLayout(); // @@ -218,7 +218,7 @@ this.mnuLimitFPS.CheckOnClick = true; this.mnuLimitFPS.Name = "mnuLimitFPS"; this.mnuLimitFPS.ShortcutKeys = System.Windows.Forms.Keys.F9; - this.mnuLimitFPS.Size = new System.Drawing.Size(152, 22); + this.mnuLimitFPS.Size = new System.Drawing.Size(150, 22); this.mnuLimitFPS.Text = "Limit FPS"; this.mnuLimitFPS.Click += new System.EventHandler(this.mnuLimitFPS_Click); // @@ -227,37 +227,67 @@ this.mnuShowFPS.CheckOnClick = true; this.mnuShowFPS.Name = "mnuShowFPS"; this.mnuShowFPS.ShortcutKeys = System.Windows.Forms.Keys.F10; - this.mnuShowFPS.Size = new System.Drawing.Size(152, 22); + this.mnuShowFPS.Size = new System.Drawing.Size(150, 22); this.mnuShowFPS.Text = "Show FPS"; this.mnuShowFPS.Click += new System.EventHandler(this.mnuShowFPS_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6); - // - // mnuInput - // - this.mnuInput.Name = "mnuInput"; - this.mnuInput.Size = new System.Drawing.Size(152, 22); - this.mnuInput.Text = "Input"; - this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); - // - // mnuVideoConfig - // - this.mnuVideoConfig.Enabled = false; - this.mnuVideoConfig.Name = "mnuVideoConfig"; - this.mnuVideoConfig.Size = new System.Drawing.Size(152, 22); - this.mnuVideoConfig.Text = "Video"; - this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); + this.toolStripMenuItem1.Size = new System.Drawing.Size(147, 6); // // mnuAudioConfig // this.mnuAudioConfig.Name = "mnuAudioConfig"; - this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22); + this.mnuAudioConfig.Size = new System.Drawing.Size(150, 22); this.mnuAudioConfig.Text = "Audio"; this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); // + // mnuInput + // + this.mnuInput.Name = "mnuInput"; + this.mnuInput.Size = new System.Drawing.Size(150, 22); + this.mnuInput.Text = "Input"; + this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); + // + // mnuRegion + // + this.mnuRegion.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuRegionAuto, + this.mnuRegionNtsc, + this.mnuRegionPal}); + this.mnuRegion.Name = "mnuRegion"; + this.mnuRegion.Size = new System.Drawing.Size(150, 22); + this.mnuRegion.Text = "Region"; + // + // mnuRegionAuto + // + this.mnuRegionAuto.Name = "mnuRegionAuto"; + this.mnuRegionAuto.Size = new System.Drawing.Size(104, 22); + this.mnuRegionAuto.Text = "Auto"; + this.mnuRegionAuto.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuRegionNtsc + // + this.mnuRegionNtsc.Name = "mnuRegionNtsc"; + this.mnuRegionNtsc.Size = new System.Drawing.Size(104, 22); + this.mnuRegionNtsc.Text = "NTSC"; + this.mnuRegionNtsc.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuRegionPal + // + this.mnuRegionPal.Name = "mnuRegionPal"; + this.mnuRegionPal.Size = new System.Drawing.Size(104, 22); + this.mnuRegionPal.Text = "PAL"; + this.mnuRegionPal.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuVideoConfig + // + this.mnuVideoConfig.Name = "mnuVideoConfig"; + this.mnuVideoConfig.Size = new System.Drawing.Size(150, 22); + this.mnuVideoConfig.Text = "Video"; + this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); + // // mnuTools // this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -441,45 +471,15 @@ this.dxViewer.Location = new System.Drawing.Point(0, 24); this.dxViewer.Margin = new System.Windows.Forms.Padding(0); this.dxViewer.Name = "dxViewer"; - this.dxViewer.Size = new System.Drawing.Size(1024, 896); + this.dxViewer.Size = new System.Drawing.Size(1024, 960); this.dxViewer.TabIndex = 1; // - // mnuRegion - // - this.mnuRegion.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.mnuRegionAuto, - this.mnuRegionNtsc, - this.mnuRegionPal}); - this.mnuRegion.Name = "mnuRegion"; - this.mnuRegion.Size = new System.Drawing.Size(152, 22); - this.mnuRegion.Text = "Region"; - // - // mnuRegionAuto - // - this.mnuRegionAuto.Name = "mnuRegionAuto"; - this.mnuRegionAuto.Size = new System.Drawing.Size(152, 22); - this.mnuRegionAuto.Text = "Auto"; - this.mnuRegionAuto.Click += new System.EventHandler(this.mnuRegion_Click); - // - // mnuRegionNtsc - // - this.mnuRegionNtsc.Name = "mnuRegionNtsc"; - this.mnuRegionNtsc.Size = new System.Drawing.Size(152, 22); - this.mnuRegionNtsc.Text = "NTSC"; - this.mnuRegionNtsc.Click += new System.EventHandler(this.mnuRegion_Click); - // - // mnuRegionPal - // - this.mnuRegionPal.Name = "mnuRegionPal"; - this.mnuRegionPal.Size = new System.Drawing.Size(152, 22); - this.mnuRegionPal.Text = "PAL"; - this.mnuRegionPal.Click += new System.EventHandler(this.mnuRegion_Click); - // // frmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.ClientSize = new System.Drawing.Size(365, 272); this.Controls.Add(this.dxViewer); this.Controls.Add(this.menuStrip); diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 69ca3ff9..2ab19a1a 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -37,8 +37,7 @@ namespace Mesen.GUI.Forms _notifListener = new InteropEmu.NotificationListener(); _notifListener.OnNotification += _notifListener_OnNotification; - mnuShowFPS.Checked = ConfigManager.Config.VideoInfo.ShowFPS; - mnuLimitFPS.Checked = ConfigManager.Config.VideoInfo.LimitFPS; + UpdateVideoSettings(); InitializeEmu(); @@ -72,6 +71,13 @@ namespace Mesen.GUI.Forms VideoInfo.ApplyConfig(); } + void UpdateVideoSettings() + { + mnuShowFPS.Checked = ConfigManager.Config.VideoInfo.ShowFPS; + mnuLimitFPS.Checked = ConfigManager.Config.VideoInfo.LimitFPS; + dxViewer.Size = VideoInfo.GetViewerSize(); + } + void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { MessageBox.Show(e.Exception.ToString(), "Unexpected Error", MessageBoxButtons.OK, MessageBoxIcon.Error); @@ -317,7 +323,8 @@ namespace Mesen.GUI.Forms private void mnuVideoConfig_Click(object sender, EventArgs e) { - + new frmVideoConfig().ShowDialog(); + UpdateVideoSettings(); } private void mnuDebugger_Click(object sender, EventArgs e) @@ -397,14 +404,12 @@ namespace Mesen.GUI.Forms private void mnuInput_Click(object sender, EventArgs e) { - frmInputConfig frm = new frmInputConfig(); - frm.ShowDialog(); + new frmInputConfig().ShowDialog(); } private void mnuAudioConfig_Click(object sender, EventArgs e) { - frmAudioConfig frm = new frmAudioConfig(); - frm.ShowDialog(); + new frmAudioConfig().ShowDialog(); } private void mnuRegion_Click(object sender, EventArgs e) diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 1608a502..5d19b855 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -66,6 +66,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SetChannelVolume(UInt32 channel, double volume); [DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency); [DllImport(DLLPath)] public static extern void SetNesModel(NesModel model); + [DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom); [DllImport(DLLPath)] public static extern void DebugInitialize(); [DllImport(DLLPath)] public static extern void DebugRelease(); diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 7c725c92..27c82013 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -12,6 +12,7 @@ #include "../Core/CheatManager.h" #include "../Core/StandardController.h" #include "../Core/EmulationSettings.h" +#include "../Core/VideoDecoder.h" static NES::Renderer *_renderer = nullptr; static SoundManager *_soundManager = nullptr; @@ -131,7 +132,7 @@ namespace InteropEmu { } DllExport void __stdcall Render() { _renderer->Render(); } - DllExport void __stdcall TakeScreenshot() { _renderer->TakeScreenshot(Console::GetROMPath()); } + DllExport void __stdcall TakeScreenshot() { VideoDecoder::GetInstance()->TakeScreenshot(FolderUtilities::GetFilename(Console::GetROMPath(), false)); } DllExport INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback) { @@ -163,6 +164,7 @@ namespace InteropEmu { DllExport void __stdcall SetChannelVolume(uint32_t channel, double volume) { EmulationSettings::SetChannelVolume((AudioChannel)channel, volume); } DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { EmulationSettings::SetAudioLatency(msLatency); } DllExport void __stdcall SetNesModel(uint32_t model) { EmulationSettings::SetNesModel((NesModel)model); } + DllExport void __stdcall SetOverscanDimensions(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) { EmulationSettings::SetOverscanDimensions(left, right, top, bottom); } } } \ No newline at end of file diff --git a/Windows/Renderer.cpp b/Windows/Renderer.cpp index caa1cd70..facff700 100644 --- a/Windows/Renderer.cpp +++ b/Windows/Renderer.cpp @@ -16,16 +16,12 @@ namespace NES { Renderer::Renderer(HWND hWnd) { - SetScreenSize(256, 240); - _hWnd = hWnd; - if(FAILED(InitDevice())) { - CleanupDevice(); - } else { - PPU::RegisterVideoDevice(this); - MessageManager::RegisterMessageManager(this); - } + SetScreenSize(); + + PPU::RegisterVideoDevice(this); + MessageManager::RegisterMessageManager(this); } Renderer::~Renderer() @@ -33,17 +29,25 @@ namespace NES CleanupDevice(); } - void Renderer::SetScreenSize(uint32_t screenWidth, uint32_t screenHeight) + void Renderer::SetScreenSize() { - _screenWidth = screenWidth; - _screenHeight = screenHeight; - _bytesPerPixel = 4; + OverscanDimensions overscan = EmulationSettings::GetOverscanDimensions(); - _hdScreenWidth = _screenWidth * 4; - _hdScreenHeight = (_screenHeight - 16) * 4; - - _screenBufferSize = _screenWidth * _screenHeight * _bytesPerPixel; - _hdScreenBufferSize = _hdScreenWidth * _hdScreenHeight * _bytesPerPixel; + if(_hdScreenBufferSize == 0 || _hdScreenBufferSize != overscan.GetPixelCount() * 64) { + uint32_t screenWidth = overscan.GetScreenWidth(); + uint32_t screenHeight = overscan.GetScreenHeight(); + + _hdScreenWidth = screenWidth * 4; + _hdScreenHeight = screenHeight * 4; + _hdScreenBufferSize = _hdScreenWidth * _hdScreenHeight * _bytesPerPixel; + + _frameLock.Acquire(); + CleanupDevice(); + if(FAILED(InitDevice())) { + CleanupDevice(); + } + _frameLock.Release(); + } } void Renderer::CleanupDevice() @@ -67,11 +71,6 @@ namespace NES _videoRAM = nullptr; } - if(_nextFrameBuffer) { - delete[] _nextFrameBuffer; - _nextFrameBuffer = nullptr; - } - if(_overlayBuffer) { delete[] _overlayBuffer; _overlayBuffer = nullptr; @@ -79,10 +78,12 @@ namespace NES if(_ppuOutputBuffer) { delete[] _ppuOutputBuffer; + _ppuOutputBuffer = nullptr; } if(_ppuOutputSecondaryBuffer) { delete[] _ppuOutputSecondaryBuffer; + _ppuOutputSecondaryBuffer = nullptr; } } @@ -228,16 +229,14 @@ namespace NES vp.TopLeftY = 0; _pDeviceContext->RSSetViewports(1, &vp); - _videoRAM = new uint8_t[_screenBufferSize]; - _nextFrameBuffer = new uint8_t[_screenBufferSize]; - _ppuOutputBuffer = new uint16_t[_screenWidth * _screenHeight]; - _ppuOutputSecondaryBuffer = new uint16_t[_screenWidth * _screenHeight]; - memset(_videoRAM, 0x00, _screenBufferSize); - memset(_nextFrameBuffer, 0x00, _screenBufferSize); - memset(_ppuOutputBuffer, 0x00, _screenWidth * _screenHeight * sizeof(uint16_t)); - memset(_ppuOutputSecondaryBuffer, 0x00, _screenWidth * _screenHeight * sizeof(uint16_t)); + _videoRAM = new uint32_t[PPU::PixelCount]; + _ppuOutputBuffer = new uint16_t[PPU::PixelCount]; + _ppuOutputSecondaryBuffer = new uint16_t[PPU::PixelCount]; + memset(_videoRAM, 0x00, PPU::PixelCount * sizeof(uint32_t)); + memset(_ppuOutputBuffer, 0x00, PPU::OutputBufferSize); + memset(_ppuOutputSecondaryBuffer, 0x00, PPU::OutputBufferSize); - _pTexture = CreateTexture(_screenWidth, _screenHeight); + _pTexture = CreateTexture(_hdScreenWidth/4, _hdScreenHeight/4); if(!_pTexture) { return 0; } @@ -380,32 +379,31 @@ namespace NES memcpy(_ppuOutputSecondaryBuffer, _ppuOutputBuffer, 256 * 240 * sizeof(uint16_t)); _frameLock.Release(); - VideoDecoder::DecodeFrame(_ppuOutputSecondaryBuffer, (uint32_t*)_nextFrameBuffer); + uint32_t* outputBuffer = VideoDecoder::GetInstance()->DecodeFrame(_ppuOutputSecondaryBuffer); - RECT sourceRect; - sourceRect.left = 0; - sourceRect.right = _screenWidth; - sourceRect.top = 8; - sourceRect.bottom = _screenHeight - 8; + OverscanDimensions overscan = EmulationSettings::GetOverscanDimensions(); RECT destRect; destRect.left = 0; destRect.top = 0; - destRect.right = _screenWidth * 4; - destRect.bottom = (_screenHeight - 16) * 4; + destRect.right = overscan.GetScreenWidth() * 4; + destRect.bottom = overscan.GetScreenHeight() * 4; XMVECTOR position{ { 0, 0 } }; D3D11_MAPPED_SUBRESOURCE dd; dd.pData = (void *)_videoRAM; - dd.RowPitch = _screenWidth * _bytesPerPixel; - dd.DepthPitch = _screenBufferSize; + dd.RowPitch = overscan.GetScreenWidth() * _bytesPerPixel; + dd.DepthPitch = overscan.GetPixelCount() * _bytesPerPixel; _pDeviceContext->Map(_pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd); - memcpy(dd.pData, _nextFrameBuffer, _screenBufferSize); + memset(dd.pData, 0, dd.DepthPitch); + for(uint32_t i = 0; i < overscan.GetScreenHeight(); i++) { + memcpy((uint8_t*)dd.pData+i*dd.RowPitch, outputBuffer+i*overscan.GetScreenWidth(), overscan.GetScreenWidth() * _bytesPerPixel); + } _pDeviceContext->Unmap(_pTexture, 0); ID3D11ShaderResourceView *nesOutputBuffer = GetShaderResourceView(_pTexture); - _spriteBatch->Draw(nesOutputBuffer, destRect, &sourceRect); + _spriteBatch->Draw(nesOutputBuffer, destRect); nesOutputBuffer->Release(); } @@ -441,6 +439,8 @@ namespace NES void Renderer::Render() { if(_frameChanged || EmulationSettings::CheckFlag(EmulationFlags::Paused) || !_toasts.empty()) { + SetScreenSize(); + _frameChanged = false; // Clear the back buffer _pDeviceContext->ClearRenderTargetView(_pRenderTargetView, Colors::Black); @@ -473,7 +473,7 @@ namespace NES } string fpsString = string("FPS: ") + std::to_string(_currentFPS); - DrawOutlinedString(fpsString, 256 * 4 - 80, 13, Colors::AntiqueWhite, 1.0f); + DrawOutlinedString(fpsString, (float)(_hdScreenWidth - 80), 13, Colors::AntiqueWhite, 1.0f); } } @@ -591,40 +591,4 @@ namespace NES _frameChanged = true; _frameCount++; } - - void Renderer::TakeScreenshot(string romFilename) - { - uint32_t* frameBuffer = new uint32_t[256 * 240]; - - _frameLock.Acquire(); - memcpy(frameBuffer, _nextFrameBuffer, 256 * 240 * 4); - _frameLock.Release(); - - //ARGB -> ABGR - for(uint32_t i = 0; i < 256 * 240; i++) { - frameBuffer[i] = (frameBuffer[i] & 0xFF00FF00) | ((frameBuffer[i] & 0xFF0000) >> 16) | ((frameBuffer[i] & 0xFF) << 16); - } - - int counter = 0; - string baseFilename = FolderUtilities::GetScreenshotFolder() + FolderUtilities::GetFilename(romFilename, false); - string ssFilename; - while(true) { - string counterStr = std::to_string(counter); - while(counterStr.length() < 3) { - counterStr = "0" + counterStr; - } - ssFilename = baseFilename + "_" + counterStr + ".png"; - ifstream file(ssFilename, ios::in); - if(file) { - file.close(); - } else { - break; - } - counter++; - } - - PNGWriter::WritePNG(ssFilename, (uint8_t*)frameBuffer, 256, 240); - MessageManager::DisplayMessage("Screenshot saved", FolderUtilities::GetFilename(ssFilename, true)); - } - } \ No newline at end of file diff --git a/Windows/Renderer.h b/Windows/Renderer.h index 3f6c4fe6..5e65ebe6 100644 --- a/Windows/Renderer.h +++ b/Windows/Renderer.h @@ -36,10 +36,9 @@ namespace NES { ID3D11SamplerState* _samplerState = nullptr; ID3D11Texture2D* _pTexture = nullptr; - byte* _videoRAM = nullptr; + uint32_t* _videoRAM = nullptr; bool _frameChanged = true; - uint8_t* _nextFrameBuffer = nullptr; uint16_t* _ppuOutputBuffer = nullptr; uint16_t* _ppuOutputSecondaryBuffer = nullptr; SimpleLock _frameLock; @@ -57,23 +56,18 @@ namespace NES { unique_ptr _spriteBatch; //ID3D11PixelShader* _pixelShader = nullptr; - uint32_t _screenWidth; - uint32_t _screenHeight; - uint32_t _bytesPerPixel; - uint32_t _hdScreenWidth; - uint32_t _hdScreenHeight; - uint32_t _screenBufferSize; - uint32_t _hdScreenBufferSize; - - uint32_t _flags = 0; + const uint32_t _bytesPerPixel = 4; + uint32_t _hdScreenWidth = 0; + uint32_t _hdScreenHeight = 0; + uint32_t _hdScreenBufferSize = 0; list> _toasts; - ID3D11ShaderResourceView* _toastTexture; + ID3D11ShaderResourceView* _toastTexture = nullptr; HRESULT InitDevice(); void CleanupDevice(); - void SetScreenSize(uint32_t screenWidth, uint32_t screenHeight); + void SetScreenSize(); ID3D11Texture2D* CreateTexture(uint32_t width, uint32_t height); ID3D11ShaderResourceView* GetShaderResourceView(ID3D11Texture2D* texture); @@ -92,27 +86,8 @@ namespace NES { ~Renderer(); void Render(); - void DisplayMessage(string title, string message); - - void SetFlags(uint32_t flags) - { - _flags |= flags; - } - - void ClearFlags(uint32_t flags) - { - _flags &= ~flags; - } - - bool CheckFlag(uint32_t flag) - { - return (_flags & flag) == flag; - } - void UpdateFrame(void* frameBuffer); void DisplayToast(shared_ptr toast); - - void TakeScreenshot(string romFilename); }; } \ No newline at end of file