mirror of
https://github.com/SourMesen/Mesen2.git
synced 2024-06-22 22:22:51 -04:00
AVI: Added options to record system/input HUDs in AVIs
This commit is contained in:
parent
d255cf3db0
commit
6f99de0135
|
@ -25,11 +25,11 @@ void DebugHud::ClearScreen()
|
|||
_commands.clear();
|
||||
}
|
||||
|
||||
void DebugHud::Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions overscan, uint32_t frameNumber, bool autoScale)
|
||||
void DebugHud::Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions overscan, uint32_t frameNumber, bool autoScale, float forcedScale)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
for(unique_ptr<DrawCommand> &command : _commands) {
|
||||
command->Draw(argbBuffer, frameInfo, overscan, frameNumber, autoScale);
|
||||
command->Draw(argbBuffer, frameInfo, overscan, frameNumber, autoScale, forcedScale);
|
||||
}
|
||||
_commands.erase(std::remove_if(_commands.begin(), _commands.end(), [](const unique_ptr<DrawCommand>& c) { return c->Expired(); }), _commands.end());
|
||||
_commandCount = (uint32_t)_commands.size();
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
|
||||
bool HasCommands() { return _commandCount > 0; }
|
||||
|
||||
void Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions overscan, uint32_t frameNumber, bool autoScale);
|
||||
void Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions overscan, uint32_t frameNumber, bool autoScale, float forcedScale = 0);
|
||||
void ClearScreen();
|
||||
|
||||
void DrawPixel(int x, int y, int color, int frameCount, int startFrame = -1);
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions &overscan, uint32_t frameNumber, bool autoScale)
|
||||
void Draw(uint32_t* argbBuffer, FrameInfo frameInfo, OverscanDimensions &overscan, uint32_t frameNumber, bool autoScale, float forcedScale = 0)
|
||||
{
|
||||
if(_startFrame < 0) {
|
||||
//When no start frame was specified, start on the next drawn frame
|
||||
|
@ -118,7 +118,10 @@ public:
|
|||
_frameInfo = frameInfo;
|
||||
_overscan = overscan;
|
||||
|
||||
if(autoScale && !_disableAutoScale) {
|
||||
if(forcedScale != 0) {
|
||||
_xScale = forcedScale;
|
||||
_yScale = forcedScale;
|
||||
} else if(autoScale && !_disableAutoScale) {
|
||||
//TODOv2 review
|
||||
float scale = _frameInfo.Width + _overscan.Left + _overscan.Right > 256 ? (_frameInfo.Width + _overscan.Left + _overscan.Right) / 256.0f : 1;
|
||||
_yScale = _frameInfo.Height + _overscan.Top + _overscan.Bottom > 240 ? (int)scale : 1;
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
#include "Shared/Video/DrawStringCommand.h"
|
||||
#include "Shared/Interfaces/IMessageManager.h"
|
||||
|
||||
SystemHud::SystemHud(Emulator* emu, DebugHud* hud)
|
||||
SystemHud::SystemHud(Emulator* emu)
|
||||
{
|
||||
_emu = emu;
|
||||
_hud = hud;
|
||||
MessageManager::RegisterMessageManager(this);
|
||||
}
|
||||
|
||||
|
@ -19,39 +18,37 @@ SystemHud::~SystemHud()
|
|||
MessageManager::UnregisterMessageManager(this);
|
||||
}
|
||||
|
||||
void SystemHud::Draw(uint32_t width, uint32_t height)
|
||||
void SystemHud::Draw(DebugHud* hud, uint32_t width, uint32_t height) const
|
||||
{
|
||||
_screenWidth = width;
|
||||
_screenHeight = height;
|
||||
DrawCounters();
|
||||
DrawMessages();
|
||||
DrawCounters(hud, width);
|
||||
DrawMessages(hud, width, height);
|
||||
|
||||
if(_emu->IsRunning()) {
|
||||
EmuSettings* settings = _emu->GetSettings();
|
||||
bool showMovieIcons = settings->GetPreferences().ShowMovieIcons;
|
||||
int xOffset = 0;
|
||||
if(_emu->IsPaused()) {
|
||||
DrawPauseIcon();
|
||||
DrawPauseIcon(hud);
|
||||
} else if(showMovieIcons && _emu->GetMovieManager()->Playing()) {
|
||||
DrawPlayIcon();
|
||||
DrawPlayIcon(hud);
|
||||
xOffset += 12;
|
||||
} else if(showMovieIcons && _emu->GetMovieManager()->Recording()) {
|
||||
DrawRecordIcon();
|
||||
DrawRecordIcon(hud);
|
||||
xOffset += 12;
|
||||
}
|
||||
|
||||
bool showTurboRewindIcons = settings->GetPreferences().ShowTurboRewindIcons;
|
||||
if(!_emu->IsPaused() && showTurboRewindIcons) {
|
||||
if(settings->CheckFlag(EmulationFlags::Rewind)) {
|
||||
DrawTurboRewindIcon(true, xOffset);
|
||||
DrawTurboRewindIcon(hud, true, xOffset);
|
||||
} else if(settings->CheckFlag(EmulationFlags::Turbo)) {
|
||||
DrawTurboRewindIcon(false, xOffset);
|
||||
DrawTurboRewindIcon(hud, false, xOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemHud::DrawMessage(MessageInfo &msg, int& lastHeight)
|
||||
|
||||
void SystemHud::DrawMessage(DebugHud* hud, MessageInfo &msg, uint32_t screenWidth, uint32_t screenHeight, int& lastHeight) const
|
||||
{
|
||||
//Get opacity for fade in/out effect
|
||||
uint8_t opacity = (uint8_t)(msg.GetOpacity() * 255);
|
||||
|
@ -59,54 +56,34 @@ void SystemHud::DrawMessage(MessageInfo &msg, int& lastHeight)
|
|||
|
||||
string text = "[" + msg.GetTitle() + "] " + msg.GetMessage();
|
||||
|
||||
int maxWidth = _screenWidth - textLeftMargin;
|
||||
int maxWidth = screenWidth - textLeftMargin;
|
||||
TextSize size = DrawStringCommand::MeasureString(text, maxWidth);
|
||||
lastHeight += size.Y;
|
||||
DrawString(text, textLeftMargin, _screenHeight - lastHeight, opacity);
|
||||
DrawString(hud, screenWidth, text, textLeftMargin, screenHeight - lastHeight, opacity);
|
||||
}
|
||||
|
||||
void SystemHud::DrawString(string text, int x, int y, uint8_t opacity)
|
||||
void SystemHud::DrawString(DebugHud* hud, uint32_t screenWidth, string text, int x, int y, uint8_t opacity) const
|
||||
{
|
||||
int maxWidth = _screenWidth - x;
|
||||
int maxWidth = screenWidth - x;
|
||||
opacity = 255 - opacity;
|
||||
for(int i = -1; i <= 1; i++) {
|
||||
for(int j = -1; j <= 1; j++) {
|
||||
_hud->DrawString(x + i, y + j, text, 0 | (opacity << 24), 0xFF000000, 1, -1, maxWidth);
|
||||
hud->DrawString(x + i, y + j, text, 0 | (opacity << 24), 0xFF000000, 1, -1, maxWidth);
|
||||
}
|
||||
}
|
||||
_hud->DrawString(x, y, text, 0xFFFFFF | (opacity << 24), 0xFF000000, 1, -1, maxWidth);
|
||||
hud->DrawString(x, y, text, 0xFFFFFF | (opacity << 24), 0xFF000000, 1, -1, maxWidth);
|
||||
}
|
||||
|
||||
void SystemHud::ShowFpsCounter(int lineNumber)
|
||||
void SystemHud::ShowFpsCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const
|
||||
{
|
||||
int yPos = 10 + 10 * lineNumber;
|
||||
if(_fpsTimer.GetElapsedMS() > 1000) {
|
||||
//Update fps every sec
|
||||
uint32_t frameCount = _emu->GetFrameCount();
|
||||
if(_lastFrameCount > frameCount) {
|
||||
_currentFPS = 0;
|
||||
} else {
|
||||
_currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
_currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
}
|
||||
_lastFrameCount = frameCount;
|
||||
_lastRenderedFrameCount = _renderedFrameCount;
|
||||
_fpsTimer.Reset();
|
||||
}
|
||||
|
||||
if(_currentFPS > 5000) {
|
||||
_currentFPS = 0;
|
||||
}
|
||||
if(_currentRenderedFPS > 5000) {
|
||||
_currentRenderedFPS = 0;
|
||||
}
|
||||
|
||||
string fpsString = string("FPS: ") + std::to_string(_currentFPS); // +" / " + std::to_string(_currentRenderedFPS);
|
||||
uint32_t length = DrawStringCommand::MeasureString(fpsString).X;
|
||||
DrawString(fpsString, _screenWidth - 8 - length, yPos);
|
||||
DrawString(hud, screenWidth, fpsString, screenWidth - 8 - length, yPos);
|
||||
}
|
||||
|
||||
void SystemHud::ShowGameTimer(int lineNumber)
|
||||
void SystemHud::ShowGameTimer(DebugHud* hud, uint32_t screenWidth, int lineNumber) const
|
||||
{
|
||||
int yPos = 10 + 10 * lineNumber;
|
||||
uint32_t frameCount = _emu->GetFrameCount();
|
||||
|
@ -122,48 +99,47 @@ void SystemHud::ShowGameTimer(int lineNumber)
|
|||
|
||||
string text = ss.str();
|
||||
uint32_t length = DrawStringCommand::MeasureString(text).X;
|
||||
DrawString(ss.str(), _screenWidth - 8 - length, yPos);
|
||||
DrawString(hud, screenWidth, ss.str(), screenWidth - 8 - length, yPos);
|
||||
}
|
||||
|
||||
void SystemHud::ShowFrameCounter(int lineNumber)
|
||||
void SystemHud::ShowFrameCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const
|
||||
{
|
||||
int yPos = 10 + 10 * lineNumber;
|
||||
uint32_t frameCount = _emu->GetFrameCount();
|
||||
|
||||
string frameCounter = MessageManager::Localize("Frame") + ": " + std::to_string(frameCount);
|
||||
uint32_t length = DrawStringCommand::MeasureString(frameCounter).X;
|
||||
DrawString(frameCounter, _screenWidth - 8 - length, yPos);
|
||||
DrawString(hud, screenWidth, frameCounter, screenWidth - 8 - length, yPos);
|
||||
}
|
||||
|
||||
void SystemHud::ShowLagCounter(int lineNumber)
|
||||
void SystemHud::ShowLagCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const
|
||||
{
|
||||
int yPos = 10 + 10 * lineNumber;
|
||||
uint32_t count = _emu->GetLagCounter();
|
||||
|
||||
string lagCounter = MessageManager::Localize("Lag") + ": " + std::to_string(count);
|
||||
uint32_t length = DrawStringCommand::MeasureString(lagCounter).X;
|
||||
DrawString(lagCounter, _screenWidth - 8 - length, yPos);
|
||||
DrawString(hud, screenWidth, lagCounter, screenWidth - 8 - length, yPos);
|
||||
}
|
||||
|
||||
void SystemHud::DrawCounters()
|
||||
void SystemHud::DrawCounters(DebugHud* hud, uint32_t screenWidth) const
|
||||
{
|
||||
int lineNumber = 0;
|
||||
PreferencesConfig cfg = _emu->GetSettings()->GetPreferences();
|
||||
|
||||
if(_emu->IsRunning()) {
|
||||
if(cfg.ShowFps) {
|
||||
ShowFpsCounter(lineNumber++);
|
||||
ShowFpsCounter(hud, screenWidth, lineNumber++);
|
||||
}
|
||||
if(cfg.ShowGameTimer) {
|
||||
ShowGameTimer(lineNumber++);
|
||||
ShowGameTimer(hud, screenWidth, lineNumber++);
|
||||
}
|
||||
if(cfg.ShowFrameCounter) {
|
||||
ShowFrameCounter(lineNumber++);
|
||||
ShowFrameCounter(hud, screenWidth, lineNumber++);
|
||||
}
|
||||
if(cfg.ShowLagCounter) {
|
||||
ShowLagCounter(lineNumber++);
|
||||
ShowLagCounter(hud, screenWidth, lineNumber++);
|
||||
}
|
||||
_renderedFrameCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,17 +149,13 @@ void SystemHud::DisplayMessage(string title, string message)
|
|||
_messages.push_front(std::make_unique<MessageInfo>(title, message, 3000));
|
||||
}
|
||||
|
||||
void SystemHud::DrawMessages()
|
||||
void SystemHud::DrawMessages(DebugHud* hud, uint32_t screenWidth, uint32_t screenHeight) const
|
||||
{
|
||||
auto lock = _msgLock.AcquireSafe();
|
||||
|
||||
_messages.remove_if([](unique_ptr<MessageInfo>& msg) { return msg->IsExpired(); });
|
||||
|
||||
int counter = 0;
|
||||
int lastHeight = 3;
|
||||
for(unique_ptr<MessageInfo>& msg : _messages) {
|
||||
for(auto& msg : _messages) {
|
||||
if(counter < 4) {
|
||||
DrawMessage(*msg.get(), lastHeight);
|
||||
DrawMessage(hud, *msg.get(), screenWidth, screenHeight, lastHeight);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -191,29 +163,29 @@ void SystemHud::DrawMessages()
|
|||
}
|
||||
}
|
||||
|
||||
void SystemHud::DrawBar(int x, int y, int width, int height)
|
||||
void SystemHud::DrawBar(DebugHud* hud, int x, int y, int width, int height) const
|
||||
{
|
||||
_hud->DrawRectangle(x, y, width, height, 0xFFFFFF, true, 1);
|
||||
_hud->DrawLine(x, y + 1, x + width, y + 1, 0x4FBECE, 1);
|
||||
_hud->DrawLine(x+1, y, x+1, y + height, 0x4FBECE, 1);
|
||||
hud->DrawRectangle(x, y, width, height, 0xFFFFFF, true, 1);
|
||||
hud->DrawLine(x, y + 1, x + width, y + 1, 0x4FBECE, 1);
|
||||
hud->DrawLine(x+1, y, x+1, y + height, 0x4FBECE, 1);
|
||||
|
||||
_hud->DrawLine(x + width - 1, y, x + width - 1, y + height, 0xCC9E22, 1);
|
||||
_hud->DrawLine(x, y + height - 1, x + width, y + height - 1, 0xCC9E22, 1);
|
||||
hud->DrawLine(x + width - 1, y, x + width - 1, y + height, 0xCC9E22, 1);
|
||||
hud->DrawLine(x, y + height - 1, x + width, y + height - 1, 0xCC9E22, 1);
|
||||
|
||||
_hud->DrawLine(x, y, x + width, y, 0x303030, 1);
|
||||
_hud->DrawLine(x, y, x, y + height, 0x303030, 1);
|
||||
hud->DrawLine(x, y, x + width, y, 0x303030, 1);
|
||||
hud->DrawLine(x, y, x, y + height, 0x303030, 1);
|
||||
|
||||
_hud->DrawLine(x + width, y, x + width, y + height, 0x303030, 1);
|
||||
_hud->DrawLine(x, y + height, x + width, y + height, 0x303030, 1);
|
||||
hud->DrawLine(x + width, y, x + width, y + height, 0x303030, 1);
|
||||
hud->DrawLine(x, y + height, x + width, y + height, 0x303030, 1);
|
||||
}
|
||||
|
||||
void SystemHud::DrawPauseIcon()
|
||||
void SystemHud::DrawPauseIcon(DebugHud* hud) const
|
||||
{
|
||||
DrawBar(10, 7, 5, 12);
|
||||
DrawBar(17, 7, 5, 12);
|
||||
DrawBar(hud, 10, 7, 5, 12);
|
||||
DrawBar(hud, 17, 7, 5, 12);
|
||||
}
|
||||
|
||||
void SystemHud::DrawPlayIcon()
|
||||
void SystemHud::DrawPlayIcon(DebugHud* hud) const
|
||||
{
|
||||
int x = 12;
|
||||
int y = 12;
|
||||
|
@ -225,37 +197,37 @@ void SystemHud::DrawPlayIcon()
|
|||
for(int i = 0; i < width; i++) {
|
||||
int left = x + i * 2;
|
||||
int top = y + i;
|
||||
_hud->DrawLine(left, top - 1, left, y + height - i + 1, borderColor, 1);
|
||||
_hud->DrawLine(left + 1, top - 1, left + 1, y + height - i + 1, borderColor, 1);
|
||||
hud->DrawLine(left, top - 1, left, y + height - i + 1, borderColor, 1);
|
||||
hud->DrawLine(left + 1, top - 1, left + 1, y + height - i + 1, borderColor, 1);
|
||||
|
||||
if(i > 0) {
|
||||
_hud->DrawLine(left, top, left, y + height - i, color, 1);
|
||||
hud->DrawLine(left, top, left, y + height - i, color, 1);
|
||||
}
|
||||
|
||||
if(i < width - 1) {
|
||||
_hud->DrawLine(left + 1, top, left + 1, y + height - i, color, 1);
|
||||
hud->DrawLine(left + 1, top, left + 1, y + height - i, color, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemHud::DrawRecordIcon()
|
||||
void SystemHud::DrawRecordIcon(DebugHud* hud) const
|
||||
{
|
||||
int x = 12;
|
||||
int y = 11;
|
||||
int borderColor = 0x00000;
|
||||
int color = 0xFF0000;
|
||||
|
||||
_hud->DrawRectangle(x + 3, y, 4, 10, borderColor, true, 1);
|
||||
_hud->DrawRectangle(x, y + 3, 10, 4, borderColor, true, 1);
|
||||
_hud->DrawRectangle(x + 2, y + 1, 6, 8, borderColor, true, 1);
|
||||
_hud->DrawRectangle(x + 1, y + 2, 8, 6, borderColor, true, 1);
|
||||
hud->DrawRectangle(x + 3, y, 4, 10, borderColor, true, 1);
|
||||
hud->DrawRectangle(x, y + 3, 10, 4, borderColor, true, 1);
|
||||
hud->DrawRectangle(x + 2, y + 1, 6, 8, borderColor, true, 1);
|
||||
hud->DrawRectangle(x + 1, y + 2, 8, 6, borderColor, true, 1);
|
||||
|
||||
_hud->DrawRectangle(x + 3, y + 1, 4, 8, color, true, 1);
|
||||
_hud->DrawRectangle(x + 2, y + 2, 6, 6, color, true, 1);
|
||||
_hud->DrawRectangle(x + 1, y + 3, 8, 4, color, true, 1);
|
||||
hud->DrawRectangle(x + 3, y + 1, 4, 8, color, true, 1);
|
||||
hud->DrawRectangle(x + 2, y + 2, 6, 6, color, true, 1);
|
||||
hud->DrawRectangle(x + 1, y + 3, 8, 4, color, true, 1);
|
||||
}
|
||||
|
||||
void SystemHud::DrawTurboRewindIcon(bool forRewind, int xOffset)
|
||||
void SystemHud::DrawTurboRewindIcon(DebugHud* hud, bool forRewind, int xOffset) const
|
||||
{
|
||||
int x = 12 + xOffset;
|
||||
int y = 12;
|
||||
|
@ -285,18 +257,51 @@ void SystemHud::DrawTurboRewindIcon(bool forRewind, int xOffset)
|
|||
for(int i = 0; i < width; i++) {
|
||||
int left = x + i*sign * 2;
|
||||
int top = y + i * 2;
|
||||
_hud->DrawLine(left, top - 2, left, y + height - i*2 + 2, borderColor, 1);
|
||||
_hud->DrawLine(left + 1 * sign, top - 1, left + 1 * sign, y + height - i*2 + 1, borderColor, 1);
|
||||
hud->DrawLine(left, top - 2, left, y + height - i*2 + 2, borderColor, 1);
|
||||
hud->DrawLine(left + 1 * sign, top - 1, left + 1 * sign, y + height - i*2 + 1, borderColor, 1);
|
||||
|
||||
if(i > 0) {
|
||||
_hud->DrawLine(left, top - 1, left, y + height + 1 - i*2, color, 1);
|
||||
hud->DrawLine(left, top - 1, left, y + height + 1 - i*2, color, 1);
|
||||
}
|
||||
|
||||
if(i < width - 1) {
|
||||
_hud->DrawLine(left + 1 * sign, top, left + 1 * sign, y + height - i*2, color, 1);
|
||||
hud->DrawLine(left + 1 * sign, top, left + 1 * sign, y + height - i*2, color, 1);
|
||||
}
|
||||
}
|
||||
|
||||
x += 6;
|
||||
}
|
||||
}
|
||||
|
||||
void SystemHud::UpdateHud()
|
||||
{
|
||||
{
|
||||
auto lock = _msgLock.AcquireSafe();
|
||||
_messages.remove_if([](unique_ptr<MessageInfo>& msg) { return msg->IsExpired(); });
|
||||
}
|
||||
|
||||
if(_emu->IsRunning()) {
|
||||
if(_fpsTimer.GetElapsedMS() > 1000) {
|
||||
//Update fps every sec
|
||||
uint32_t frameCount = _emu->GetFrameCount();
|
||||
if(_lastFrameCount > frameCount) {
|
||||
_currentFPS = 0;
|
||||
} else {
|
||||
_currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
_currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
}
|
||||
_lastFrameCount = frameCount;
|
||||
_lastRenderedFrameCount = _renderedFrameCount;
|
||||
_fpsTimer.Reset();
|
||||
}
|
||||
|
||||
if(_currentFPS > 5000) {
|
||||
_currentFPS = 0;
|
||||
}
|
||||
if(_currentRenderedFPS > 5000) {
|
||||
_currentRenderedFPS = 0;
|
||||
}
|
||||
|
||||
_renderedFrameCount++;
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ class SystemHud final : public IMessageManager
|
|||
{
|
||||
private:
|
||||
Emulator* _emu = nullptr;
|
||||
DebugHud* _hud = nullptr;
|
||||
|
||||
SimpleLock _msgLock;
|
||||
list<unique_ptr<MessageInfo>> _messages;
|
||||
|
@ -27,31 +26,29 @@ private:
|
|||
uint32_t _currentRenderedFPS = 0;
|
||||
uint32_t _renderedFrameCount = 0;
|
||||
|
||||
uint32_t _screenWidth = 0;
|
||||
uint32_t _screenHeight = 0;
|
||||
|
||||
void DrawMessages();
|
||||
void DrawBar(int x, int y, int width, int height);
|
||||
void DrawPauseIcon();
|
||||
void DrawPlayIcon();
|
||||
void DrawRecordIcon();
|
||||
void DrawTurboRewindIcon(bool forRewind, int xOffset);
|
||||
void DrawMessage(MessageInfo& msg, int& lastHeight);
|
||||
void DrawString(string msg, int x, int y, uint8_t opacity = 255);
|
||||
void DrawMessages(DebugHud* hud, uint32_t screenWidth, uint32_t screenHeight) const;
|
||||
void DrawBar(DebugHud* hud, int x, int y, int width, int height) const;
|
||||
void DrawPauseIcon(DebugHud* hud) const;
|
||||
void DrawPlayIcon(DebugHud* hud) const;
|
||||
void DrawRecordIcon(DebugHud* hud) const;
|
||||
void DrawTurboRewindIcon(DebugHud* hud, bool forRewind, int xOffset) const;
|
||||
void DrawMessage(DebugHud* hud, MessageInfo& msg, uint32_t screenWidth, uint32_t screenHeight, int& lastHeight) const;
|
||||
void DrawString(DebugHud* hud, uint32_t screenWidth, string msg, int x, int y, uint8_t opacity = 255) const;
|
||||
void DisplayMessage(string title, string message) override;
|
||||
|
||||
void ShowFpsCounter(int lineNumber);
|
||||
void ShowFrameCounter(int lineNumber);
|
||||
void ShowLagCounter(int lineNumber);
|
||||
void ShowGameTimer(int lineNumber);
|
||||
void ShowFpsCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const;
|
||||
void ShowFrameCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const;
|
||||
void ShowLagCounter(DebugHud* hud, uint32_t screenWidth, int lineNumber) const;
|
||||
void ShowGameTimer(DebugHud* hud, uint32_t screenWidth, int lineNumber) const;
|
||||
|
||||
void DrawCounters();
|
||||
void DrawCounters(DebugHud* hud, uint32_t screenWidth) const;
|
||||
|
||||
public:
|
||||
SystemHud(Emulator* emu, DebugHud* hud);
|
||||
SystemHud(Emulator* emu);
|
||||
~SystemHud();
|
||||
|
||||
void Draw(uint32_t width, uint32_t height);
|
||||
void Draw(DebugHud* hud, uint32_t width, uint32_t height) const;
|
||||
void UpdateHud();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ VideoRenderer::VideoRenderer(Emulator* emu)
|
|||
_stopFlag = false;
|
||||
|
||||
_rendererHud.reset(new DebugHud());
|
||||
_systemHud.reset(new SystemHud(_emu, _rendererHud.get()));
|
||||
_systemHud.reset(new SystemHud(_emu));
|
||||
_inputHud.reset(new InputHud(emu, _rendererHud.get()));
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,10 @@ void VideoRenderer::RenderThread()
|
|||
|
||||
_emuHudSurface.Clear();
|
||||
_inputHud->DrawControllers(size, frame.InputData);
|
||||
_systemHud->Draw(size.Width, size.Height);
|
||||
{
|
||||
auto lock = _hudLock.AcquireSafe();
|
||||
_systemHud->Draw(_rendererHud.get(), size.Width, size.Height);
|
||||
}
|
||||
_rendererHud->Draw(_emuHudSurface.Buffer, size, {}, 0, false);
|
||||
|
||||
DrawScriptHud(frame);
|
||||
|
@ -133,13 +136,13 @@ std::pair<FrameInfo, OverscanDimensions> VideoRenderer::GetScriptHudSize()
|
|||
|
||||
void VideoRenderer::UpdateFrame(RenderedFrame& frame)
|
||||
{
|
||||
shared_ptr<IVideoRecorder> recorder = _recorder.lock();
|
||||
if(recorder) {
|
||||
if(!recorder->AddFrame(frame.FrameBuffer, frame.Width, frame.Height, _emu->GetFps())) {
|
||||
StopRecording();
|
||||
}
|
||||
{
|
||||
auto lock = _hudLock.AcquireSafe();
|
||||
_systemHud->UpdateHud();
|
||||
}
|
||||
|
||||
ProcessAviRecording(frame);
|
||||
|
||||
{
|
||||
auto lock = _frameLock.AcquireSafe();
|
||||
_lastFrame = frame;
|
||||
|
@ -172,15 +175,58 @@ void VideoRenderer::UnregisterRenderingDevice(IRenderingDevice *renderer)
|
|||
}
|
||||
}
|
||||
|
||||
void VideoRenderer::StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel)
|
||||
void VideoRenderer::ProcessAviRecording(RenderedFrame& frame)
|
||||
{
|
||||
shared_ptr<IVideoRecorder> recorder = _recorder.lock();
|
||||
if(recorder) {
|
||||
if(_recorderOptions.RecordInputHud || _recorderOptions.RecordSystemHud) {
|
||||
//Calculate the scale needed for the HUD elements
|
||||
FrameInfo originalSize = _emu->GetVideoDecoder()->GetBaseFrameInfo(true);
|
||||
double scale = (double)frame.Height / originalSize.Height;
|
||||
FrameInfo scaledFrameSize = { (uint32_t)(frame.Width / scale), (uint32_t)(frame.Height / scale) };
|
||||
|
||||
//Update the surface to match the frame's size
|
||||
_aviRecorderSurface.UpdateSize(frame.Width, frame.Height);
|
||||
|
||||
//Copy the game screen
|
||||
memcpy(_aviRecorderSurface.Buffer, frame.FrameBuffer, frame.Width * frame.Height * sizeof(uint32_t));
|
||||
|
||||
//Draw the system/input HUDs
|
||||
DebugHud hud;
|
||||
InputHud inputHud(_emu, &hud);
|
||||
if(_recorderOptions.RecordSystemHud) {
|
||||
_systemHud->Draw(&hud, scaledFrameSize.Width, scaledFrameSize.Height);
|
||||
}
|
||||
if(_recorderOptions.RecordInputHud) {
|
||||
inputHud.DrawControllers(scaledFrameSize, frame.InputData);
|
||||
}
|
||||
|
||||
FrameInfo frameSize = { frame.Width, frame.Height };
|
||||
hud.Draw((uint32_t*)_aviRecorderSurface.Buffer, frameSize, {}, frame.FrameNumber, false, scale);
|
||||
|
||||
//Record the final result
|
||||
if(!recorder->AddFrame(_aviRecorderSurface.Buffer, frame.Width, frame.Height, _emu->GetFps())) {
|
||||
StopRecording();
|
||||
}
|
||||
} else {
|
||||
//Only record the game screen
|
||||
if(!recorder->AddFrame(frame.FrameBuffer, frame.Width, frame.Height, _emu->GetFps())) {
|
||||
StopRecording();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoRenderer::StartRecording(string filename, RecordAviOptions options)
|
||||
{
|
||||
_recorderOptions = options;
|
||||
FrameInfo frameInfo = _emu->GetVideoDecoder()->GetFrameInfo();
|
||||
|
||||
shared_ptr<IVideoRecorder> recorder;
|
||||
if(codec == VideoCodec::GIF) {
|
||||
if(options.Codec == VideoCodec::GIF) {
|
||||
recorder.reset(new GifRecorder());
|
||||
} else {
|
||||
recorder.reset(new AviRecorder(codec, compressionLevel));
|
||||
recorder.reset(new AviRecorder(options.Codec, options.CompressionLevel));
|
||||
}
|
||||
|
||||
if(recorder->StartRecording(filename, frameInfo.Width, frameInfo.Height, 4, _emu->GetSettings()->GetAudioConfig().SampleRate, _emu->GetFps())) {
|
||||
|
@ -205,6 +251,7 @@ void VideoRenderer::StopRecording()
|
|||
if(recorder) {
|
||||
MessageManager::DisplayMessage("VideoRecorder", "VideoRecorderStopped", recorder->GetOutputFile());
|
||||
}
|
||||
_aviRecorderSurface.UpdateSize(0, 0);
|
||||
_recorder.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,14 @@ class InputHud;
|
|||
class IVideoRecorder;
|
||||
enum class VideoCodec;
|
||||
|
||||
struct RecordAviOptions
|
||||
{
|
||||
VideoCodec Codec;
|
||||
uint32_t CompressionLevel;
|
||||
bool RecordSystemHud;
|
||||
bool RecordInputHud;
|
||||
};
|
||||
|
||||
class VideoRenderer
|
||||
{
|
||||
private:
|
||||
|
@ -34,7 +42,11 @@ private:
|
|||
unique_ptr<DebugHud> _rendererHud;
|
||||
unique_ptr<SystemHud> _systemHud;
|
||||
unique_ptr<InputHud> _inputHud;
|
||||
|
||||
SimpleLock _hudLock;
|
||||
|
||||
RenderSurfaceInfo _aviRecorderSurface = {};
|
||||
RecordAviOptions _recorderOptions = {};
|
||||
|
||||
RenderSurfaceInfo _emuHudSurface = {};
|
||||
RenderSurfaceInfo _scriptHudSurface = {};
|
||||
bool _needScriptHudClear = false;
|
||||
|
@ -48,6 +60,8 @@ private:
|
|||
|
||||
void RenderThread();
|
||||
void DrawScriptHud(RenderedFrame& frame);
|
||||
|
||||
void ProcessAviRecording(RenderedFrame& frame);
|
||||
|
||||
public:
|
||||
VideoRenderer(Emulator* emu);
|
||||
|
@ -67,7 +81,7 @@ public:
|
|||
void RegisterRenderingDevice(IRenderingDevice *renderer);
|
||||
void UnregisterRenderingDevice(IRenderingDevice *renderer);
|
||||
|
||||
void StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel);
|
||||
void StartRecording(string filename, RecordAviOptions options);
|
||||
void AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
|
||||
void StopRecording();
|
||||
bool IsRecording();
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
#include "Core/Shared/Movies/MovieManager.h"
|
||||
|
||||
extern unique_ptr<Emulator> _emu;
|
||||
enum class VideoCodec;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
DllExport void __stdcall AviRecord(char* filename, VideoCodec codec, uint32_t compressionLevel) { _emu->GetVideoRenderer()->StartRecording(filename, codec, compressionLevel); }
|
||||
DllExport void __stdcall AviRecord(char* filename, RecordAviOptions options) { _emu->GetVideoRenderer()->StartRecording(filename, options); }
|
||||
DllExport void __stdcall AviStop() { _emu->GetVideoRenderer()->StopRecording(); }
|
||||
DllExport bool __stdcall AviIsRecording() { return _emu->GetVideoRenderer()->IsRecording(); }
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace Mesen.Config
|
|||
{
|
||||
[Reactive] public VideoCodec Codec { get; set; } = VideoCodec.CSCD;
|
||||
[Reactive] public UInt32 CompressionLevel { get; set; } = 6;
|
||||
[Reactive] public bool RecordSystemHud { get; set; } = false;
|
||||
[Reactive] public bool RecordInputHud { get; set; } = false;
|
||||
}
|
||||
|
||||
public enum VideoCodec
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Mesen.Interop
|
|||
{
|
||||
private const string DllPath = EmuApi.DllName;
|
||||
|
||||
[DllImport(DllPath)] public static extern void AviRecord([MarshalAs(UnmanagedType.LPUTF8Str)]string filename, VideoCodec codec, UInt32 compressionLevel);
|
||||
[DllImport(DllPath)] public static extern void AviRecord([MarshalAs(UnmanagedType.LPUTF8Str)]string filename, RecordAviOptions options);
|
||||
[DllImport(DllPath)] public static extern void AviStop();
|
||||
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool AviIsRecording();
|
||||
|
||||
|
@ -68,4 +68,13 @@ namespace Mesen.Interop
|
|||
|
||||
public RecordMovieFrom RecordFrom;
|
||||
}
|
||||
|
||||
public struct RecordAviOptions
|
||||
{
|
||||
public VideoCodec Codec;
|
||||
public UInt32 CompressionLevel;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool RecordSystemHud;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool RecordInputHud;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -640,6 +640,10 @@
|
|||
<Control ID="lblCompressionLevel">Compression Level:</Control>
|
||||
<Control ID="lblLowCompression">low (fast)</Control>
|
||||
<Control ID="lblHighCompression">high (slow)</Control>
|
||||
|
||||
<Control ID="lblRecordSystemHud">Record system HUD (game timer, on-screen messages, etc.)</Control>
|
||||
<Control ID="lblRecordInputHud">Record input HUD</Control>
|
||||
|
||||
<Control ID="btnBrowse">Browse...</Control>
|
||||
<Control ID="btnOK">OK</Control>
|
||||
<Control ID="btnCancel">Cancel</Control>
|
||||
|
|
|
@ -168,7 +168,12 @@ namespace Mesen.Utilities
|
|||
RecordApi.AviStop();
|
||||
} else {
|
||||
string filename = GetOutputFilename(ConfigManager.AviFolder, ConfigManager.Config.VideoRecord.Codec == VideoCodec.GIF ? ".gif" : ".avi");
|
||||
RecordApi.AviRecord(filename, ConfigManager.Config.VideoRecord.Codec, ConfigManager.Config.VideoRecord.CompressionLevel);
|
||||
RecordApi.AviRecord(filename, new RecordAviOptions() {
|
||||
Codec = ConfigManager.Config.VideoRecord.Codec,
|
||||
CompressionLevel = ConfigManager.Config.VideoRecord.CompressionLevel,
|
||||
RecordSystemHud = ConfigManager.Config.VideoRecord.RecordSystemHud,
|
||||
RecordInputHud = ConfigManager.Config.VideoRecord.RecordInputHud
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
xmlns:vm="using:Mesen.ViewModels"
|
||||
xmlns:l="using:Mesen.Localization"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="130"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Mesen.Windows.VideoRecordWindow"
|
||||
Width="500" Height="130"
|
||||
Width="500" Height="170"
|
||||
x:DataType="vm:VideoRecordConfigViewModel"
|
||||
Title="{l:Translate wndTitle}"
|
||||
>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<Button Width="70" HorizontalContentAlignment="Center" IsCancel="True" Click="Cancel_OnClick" Content="{l:Translate btnCancel}" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="Auto,1*,Auto" RowDefinitions="Auto,Auto,Auto">
|
||||
<Grid ColumnDefinitions="Auto,1*,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
<TextBlock Text="{l:Translate lblAviFile}" />
|
||||
<TextBox Grid.Column="1" IsReadOnly="True" Text="{CompiledBinding SavePath}" />
|
||||
<Button Grid.Column="2" Content="{l:Translate btnBrowse}" Click="OnBrowseClick" />
|
||||
|
@ -46,6 +46,9 @@
|
|||
/>
|
||||
<TextBlock Grid.Column="2" Text="{l:Translate lblHighCompression}" />
|
||||
</Grid>
|
||||
|
||||
<CheckBox Grid.Row="3" Grid.ColumnSpan="3" Content="{l:Translate lblRecordSystemHud}" IsChecked="{CompiledBinding Config.RecordSystemHud}" />
|
||||
<CheckBox Grid.Row="4" Grid.ColumnSpan="3" Content="{l:Translate lblRecordInputHud}" IsChecked="{CompiledBinding Config.RecordInputHud}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
|
|
@ -45,7 +45,12 @@ namespace Mesen.Windows
|
|||
VideoRecordConfigViewModel model = (VideoRecordConfigViewModel)DataContext!;
|
||||
model.SaveConfig();
|
||||
|
||||
RecordApi.AviRecord(model.SavePath, model.Config.Codec, model.Config.CompressionLevel);
|
||||
RecordApi.AviRecord(model.SavePath, new RecordAviOptions() {
|
||||
Codec = model.Config.Codec,
|
||||
CompressionLevel = model.Config.CompressionLevel,
|
||||
RecordSystemHud = model.Config.RecordSystemHud,
|
||||
RecordInputHud = model.Config.RecordInputHud
|
||||
});
|
||||
}
|
||||
|
||||
private void Cancel_OnClick(object sender, RoutedEventArgs e)
|
||||
|
|
|
@ -16,14 +16,14 @@ void Timer::Reset()
|
|||
_start = high_resolution_clock::now();
|
||||
}
|
||||
|
||||
double Timer::GetElapsedMS()
|
||||
double Timer::GetElapsedMS() const
|
||||
{
|
||||
high_resolution_clock::time_point end = high_resolution_clock::now();
|
||||
duration<double> span = duration_cast<duration<double>>(end - _start);
|
||||
return span.count() * 1000.0;
|
||||
}
|
||||
|
||||
void Timer::WaitUntil(double targetMillisecond)
|
||||
void Timer::WaitUntil(double targetMillisecond) const
|
||||
{
|
||||
if(targetMillisecond > 0) {
|
||||
double elapsedTime = GetElapsedMS();
|
||||
|
|
|
@ -11,6 +11,6 @@ class Timer
|
|||
public:
|
||||
Timer();
|
||||
void Reset();
|
||||
double GetElapsedMS();
|
||||
void WaitUntil(double targetMillisecond);
|
||||
double GetElapsedMS() const;
|
||||
void WaitUntil(double targetMillisecond) const;
|
||||
};
|
Loading…
Reference in a new issue