2019-03-31 14:50:12 -04:00
|
|
|
|
#include "SdlRenderer.h"
|
|
|
|
|
#include "../Core/Console.h"
|
|
|
|
|
#include "../Core/Debugger.h"
|
|
|
|
|
#include "../Core/VideoRenderer.h"
|
|
|
|
|
#include "../Core/VideoDecoder.h"
|
|
|
|
|
#include "../Core/EmuSettings.h"
|
|
|
|
|
#include "../Core/MessageManager.h"
|
|
|
|
|
|
|
|
|
|
SimpleLock SdlRenderer::_frameLock;
|
|
|
|
|
|
|
|
|
|
SdlRenderer::SdlRenderer(shared_ptr<Console> console, void* windowHandle, bool registerAsMessageManager) : BaseRenderer(console, registerAsMessageManager), _windowHandle(windowHandle)
|
|
|
|
|
{
|
|
|
|
|
_frameBuffer = nullptr;
|
2019-12-28 20:54:25 +00:00
|
|
|
|
_requiredWidth = 256;
|
|
|
|
|
_requiredHeight = 240;
|
|
|
|
|
|
|
|
|
|
shared_ptr<VideoRenderer> videoRenderer = _console->GetVideoRenderer();
|
|
|
|
|
if(videoRenderer) {
|
|
|
|
|
_console->GetVideoRenderer()->RegisterRenderingDevice(this);
|
|
|
|
|
}
|
2019-03-31 14:50:12 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SdlRenderer::~SdlRenderer()
|
|
|
|
|
{
|
|
|
|
|
shared_ptr<VideoRenderer> videoRenderer = _console->GetVideoRenderer();
|
|
|
|
|
if(videoRenderer) {
|
|
|
|
|
videoRenderer->UnregisterRenderingDevice(this);
|
|
|
|
|
}
|
|
|
|
|
Cleanup();
|
2019-12-28 20:54:25 +00:00
|
|
|
|
Cleanup();
|
|
|
|
|
delete[] _frameBuffer;
|
2019-03-31 14:50:12 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight)
|
|
|
|
|
{
|
|
|
|
|
//TODO: Implement exclusive fullscreen for Linux
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SdlRenderer::Init()
|
|
|
|
|
{
|
|
|
|
|
auto log = [](const char* msg) {
|
|
|
|
|
MessageManager::Log(msg);
|
|
|
|
|
MessageManager::Log(SDL_GetError());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
|
|
|
log("[SDL] Failed to initialize video subsystem.");
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_sdlWindow = SDL_CreateWindowFrom(_windowHandle);
|
|
|
|
|
if(!_sdlWindow) {
|
|
|
|
|
log("[SDL] Failed to create window from handle.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Hack to make this work properly - otherwise SDL_CreateRenderer never returns
|
|
|
|
|
_sdlWindow->flags |= SDL_WINDOW_OPENGL;
|
|
|
|
|
|
|
|
|
|
if(SDL_GL_LoadLibrary(NULL) != 0) {
|
|
|
|
|
log("[SDL] Failed to initialize OpenGL, attempting to continue with initialization.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t baseFlags = _vsyncEnabled ? SDL_RENDERER_PRESENTVSYNC : 0;
|
|
|
|
|
|
|
|
|
|
_sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, baseFlags | SDL_RENDERER_ACCELERATED);
|
|
|
|
|
if(!_sdlRenderer) {
|
|
|
|
|
log("[SDL] Failed to create accelerated renderer.");
|
|
|
|
|
|
|
|
|
|
MessageManager::Log("[SDL] Attempting to create software renderer...");
|
|
|
|
|
_sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, baseFlags | SDL_RENDERER_SOFTWARE);
|
|
|
|
|
if(!_sdlRenderer) {
|
|
|
|
|
log("[SDL] Failed to create software renderer.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _nesFrameWidth, _nesFrameHeight);
|
|
|
|
|
if(!_sdlTexture) {
|
2019-12-28 20:54:25 +00:00
|
|
|
|
string msg = "[SDL] Failed to create texture: " + std::to_string(_nesFrameWidth) + "x" + std::to_string(_nesFrameHeight);
|
|
|
|
|
log(msg.c_str());
|
2019-03-31 14:50:12 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_spriteFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.24.spritefont"));
|
|
|
|
|
_largeFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.64.spritefont"));
|
|
|
|
|
|
|
|
|
|
SDL_SetWindowSize(_sdlWindow, _screenWidth, _screenHeight);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::Cleanup()
|
|
|
|
|
{
|
|
|
|
|
if(_sdlTexture) {
|
|
|
|
|
SDL_DestroyTexture(_sdlTexture);
|
|
|
|
|
_sdlTexture = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if(_sdlRenderer) {
|
|
|
|
|
SDL_DestroyRenderer(_sdlRenderer);
|
|
|
|
|
_sdlRenderer = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::Reset()
|
|
|
|
|
{
|
|
|
|
|
Cleanup();
|
|
|
|
|
if(Init()) {
|
|
|
|
|
_console->GetVideoRenderer()->RegisterRenderingDevice(this);
|
|
|
|
|
} else {
|
|
|
|
|
Cleanup();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
|
|
|
|
|
{
|
|
|
|
|
ScreenSize screenSize = _console->GetVideoDecoder()->GetScreenSize(false);
|
|
|
|
|
|
|
|
|
|
VideoConfig cfg = _console->GetSettings()->GetVideoConfig();
|
|
|
|
|
if(_screenHeight != (uint32_t)screenSize.Height || _screenWidth != (uint32_t)screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _useBilinearInterpolation != cfg.UseBilinearInterpolation || _vsyncEnabled != cfg.VerticalSync) {
|
|
|
|
|
_vsyncEnabled = cfg.VerticalSync;
|
|
|
|
|
_useBilinearInterpolation = cfg.UseBilinearInterpolation;
|
|
|
|
|
|
|
|
|
|
_nesFrameHeight = height;
|
|
|
|
|
_nesFrameWidth = width;
|
|
|
|
|
_newFrameBufferSize = width*height;
|
|
|
|
|
|
|
|
|
|
_screenHeight = screenSize.Height;
|
|
|
|
|
_screenWidth = screenSize.Width;
|
|
|
|
|
|
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _useBilinearInterpolation ? "1" : "0");
|
|
|
|
|
_screenBufferSize = _screenHeight*_screenWidth;
|
|
|
|
|
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
|
|
|
|
|
{
|
|
|
|
|
_frameLock.Acquire();
|
2019-12-28 20:54:25 +00:00
|
|
|
|
if(_frameBuffer == nullptr || _requiredWidth != width || _requiredHeight != height) {
|
|
|
|
|
_requiredWidth = width;
|
|
|
|
|
_requiredHeight = height;
|
|
|
|
|
|
|
|
|
|
delete[] _frameBuffer;
|
|
|
|
|
_frameBuffer = new uint32_t[width*height];
|
|
|
|
|
memset(_frameBuffer, 0, width*height*4);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 14:50:12 -04:00
|
|
|
|
memcpy(_frameBuffer, frameBuffer, width*height*_bytesPerPixel);
|
|
|
|
|
_frameChanged = true;
|
|
|
|
|
_frameLock.Release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::Render()
|
|
|
|
|
{
|
2019-12-28 20:54:25 +00:00
|
|
|
|
SetScreenSize(_requiredWidth, _requiredHeight);
|
|
|
|
|
|
2019-03-31 14:50:12 -04:00
|
|
|
|
if(!_sdlRenderer || !_sdlTexture) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool paused = _console->IsPaused() && _console->IsRunning();
|
|
|
|
|
|
|
|
|
|
if(_noUpdateCount > 10 || _frameChanged || paused || IsMessageShown()) {
|
|
|
|
|
SDL_RenderClear(_sdlRenderer);
|
|
|
|
|
|
|
|
|
|
uint8_t *textureBuffer;
|
|
|
|
|
int rowPitch;
|
|
|
|
|
SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch);
|
|
|
|
|
{
|
|
|
|
|
auto frameLock = _frameLock.AcquireSafe();
|
2019-12-28 20:54:25 +00:00
|
|
|
|
if(_frameBuffer && _nesFrameWidth == _requiredWidth && _nesFrameHeight == _requiredHeight) {
|
|
|
|
|
uint32_t* ppuFrameBuffer = _frameBuffer;
|
|
|
|
|
for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) {
|
|
|
|
|
memcpy(textureBuffer, ppuFrameBuffer, _nesFrameWidth*_bytesPerPixel);
|
|
|
|
|
ppuFrameBuffer += _nesFrameWidth;
|
|
|
|
|
textureBuffer += rowPitch;
|
|
|
|
|
}
|
2019-03-31 14:50:12 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SDL_UnlockTexture(_sdlTexture);
|
|
|
|
|
|
|
|
|
|
if(_frameChanged) {
|
|
|
|
|
_renderedFrameCount++;
|
|
|
|
|
_frameChanged = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Rect source = {0, 0, (int)_nesFrameWidth, (int)_nesFrameHeight };
|
|
|
|
|
SDL_Rect dest = {0, 0, (int)_screenWidth, (int)_screenHeight };
|
|
|
|
|
SDL_RenderCopy(_sdlRenderer, _sdlTexture, &source, &dest);
|
|
|
|
|
|
2019-08-04 22:27:09 -04:00
|
|
|
|
if(_console->IsRunning()) {
|
2019-07-16 19:08:16 -04:00
|
|
|
|
if(paused) {
|
|
|
|
|
DrawPauseScreen();
|
|
|
|
|
}
|
2019-03-31 14:50:12 -04:00
|
|
|
|
DrawCounters();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DrawToasts();
|
|
|
|
|
|
|
|
|
|
SDL_RenderPresent(_sdlRenderer);
|
|
|
|
|
} else {
|
|
|
|
|
_noUpdateCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::DrawPauseScreen()
|
|
|
|
|
{
|
2019-07-18 17:26:48 -04:00
|
|
|
|
DrawString(L"I", 15, 15, 255, 153, 0, 168);
|
|
|
|
|
DrawString(L"I", 23, 15, 255, 153, 0, 168);
|
2019-03-31 14:50:12 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SdlRenderer::DrawString(std::wstring message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity)
|
|
|
|
|
{
|
|
|
|
|
const wchar_t *text = message.c_str();
|
|
|
|
|
_spriteFont->DrawString(_sdlRenderer, text, x, y, r, g, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float SdlRenderer::MeasureString(std::wstring text)
|
|
|
|
|
{
|
|
|
|
|
return _spriteFont->MeasureString(text.c_str()).x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SdlRenderer::ContainsCharacter(wchar_t character)
|
|
|
|
|
{
|
|
|
|
|
return _spriteFont->ContainsCharacter(character);
|
2019-12-28 20:54:25 +00:00
|
|
|
|
}
|