mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
269 lines
7.5 KiB
C++
269 lines
7.5 KiB
C++
#include "SdlRenderer.h"
|
|
#include "Core/Debugger/Debugger.h"
|
|
#include "Core/Shared/Emulator.h"
|
|
#include "Core/Shared/Video/VideoRenderer.h"
|
|
#include "Core/Shared/Video/VideoDecoder.h"
|
|
#include "Core/Shared/EmuSettings.h"
|
|
#include "Core/Shared/MessageManager.h"
|
|
#include "Core/Shared/RenderedFrame.h"
|
|
|
|
SimpleLock SdlRenderer::_frameLock;
|
|
|
|
SdlRenderer::SdlRenderer(Emulator* emu, void* windowHandle) : _windowHandle(windowHandle)
|
|
{
|
|
_emu = emu;
|
|
_frameBuffer = nullptr;
|
|
_requiredWidth = 256;
|
|
_requiredHeight = 240;
|
|
|
|
_emu->GetVideoRenderer()->RegisterRenderingDevice(this);
|
|
}
|
|
|
|
SdlRenderer::~SdlRenderer()
|
|
{
|
|
_emu->GetVideoRenderer()->UnregisterRenderingDevice(this);
|
|
|
|
Cleanup();
|
|
delete[] _frameBuffer;
|
|
}
|
|
|
|
void SdlRenderer::LogSdlError(const char* msg)
|
|
{
|
|
MessageManager::Log(msg);
|
|
MessageManager::Log(SDL_GetError());
|
|
}
|
|
|
|
void SdlRenderer::SetExclusiveFullscreenMode(bool fullscreen, void* windowHandle)
|
|
{
|
|
//TODO: Implement exclusive fullscreen for Linux
|
|
}
|
|
|
|
bool SdlRenderer::Init()
|
|
{
|
|
if(SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
|
LogSdlError("[SDL] Failed to initialize video subsystem.");
|
|
return false;
|
|
};
|
|
|
|
_sdlWindow = SDL_CreateWindowFrom(_windowHandle);
|
|
if(!_sdlWindow) {
|
|
LogSdlError("[SDL] Failed to create window from handle.");
|
|
return false;
|
|
}
|
|
|
|
if(SDL_GL_LoadLibrary(NULL) != 0) {
|
|
LogSdlError("[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) {
|
|
LogSdlError("[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) {
|
|
LogSdlError("[SDL] Failed to create software renderer.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SdlRenderer::InitTexture()
|
|
{
|
|
_sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _frameWidth, _frameHeight);
|
|
if(!_sdlTexture) {
|
|
string msg = "[SDL] Failed to create texture: " + std::to_string(_frameWidth) + "x" + std::to_string(_frameHeight);
|
|
LogSdlError(msg.c_str());
|
|
return false;
|
|
}
|
|
|
|
SDL_SetWindowSize(_sdlWindow, _screenWidth, _screenHeight);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SdlRenderer::Cleanup()
|
|
{
|
|
if(_sdlTexture) {
|
|
SDL_DestroyTexture(_sdlTexture);
|
|
_sdlTexture = nullptr;
|
|
}
|
|
if(_emuHud.Texture) {
|
|
SDL_DestroyTexture(_emuHud.Texture);
|
|
_emuHud.Texture = nullptr;
|
|
}
|
|
if(_scriptHud.Texture) {
|
|
SDL_DestroyTexture(_scriptHud.Texture);
|
|
_scriptHud.Texture = nullptr;
|
|
}
|
|
if(_sdlRenderer) {
|
|
SDL_DestroyRenderer(_sdlRenderer);
|
|
_sdlRenderer = nullptr;
|
|
}
|
|
}
|
|
|
|
void SdlRenderer::OnRendererThreadStarted()
|
|
{
|
|
//SDL stops working if the rendering moves to a new thread
|
|
//Reset everything to make it work again
|
|
Reset();
|
|
}
|
|
|
|
void SdlRenderer::Reset()
|
|
{
|
|
Cleanup();
|
|
if(Init()) {
|
|
InitTexture();
|
|
_emu->GetVideoRenderer()->RegisterRenderingDevice(this);
|
|
} else {
|
|
Cleanup();
|
|
}
|
|
}
|
|
|
|
void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
|
|
{
|
|
VideoConfig cfg = _emu->GetSettings()->GetVideoConfig();
|
|
FrameInfo size = _emu->GetVideoRenderer()->GetRendererSize();
|
|
if(_screenHeight != size.Height || _screenWidth != size.Width || _frameHeight != height || _frameWidth != width || _useBilinearInterpolation != cfg.UseBilinearInterpolation || _vsyncEnabled != cfg.VerticalSync) {
|
|
_vsyncEnabled = cfg.VerticalSync;
|
|
_useBilinearInterpolation = cfg.UseBilinearInterpolation;
|
|
|
|
_frameHeight = height;
|
|
_frameWidth = width;
|
|
_newFrameBufferSize = width*height;
|
|
|
|
_screenHeight = size.Height;
|
|
_screenWidth = size.Width;
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _useBilinearInterpolation ? "1" : "0");
|
|
_screenBufferSize = _screenHeight*_screenWidth;
|
|
|
|
Reset();
|
|
}
|
|
}
|
|
|
|
void SdlRenderer::ClearFrame()
|
|
{
|
|
auto lock = _frameLock.AcquireSafe();
|
|
if(_frameBuffer == nullptr) {
|
|
return;
|
|
}
|
|
|
|
memset(_frameBuffer, 0, _requiredWidth * _requiredHeight * _bytesPerPixel);
|
|
_frameChanged = true;
|
|
}
|
|
|
|
void SdlRenderer::UpdateFrame(RenderedFrame& frame)
|
|
{
|
|
auto lock = _frameLock.AcquireSafe();
|
|
if(_frameBuffer == nullptr || _requiredWidth != frame.Width || _requiredHeight != frame.Height) {
|
|
_requiredWidth = frame.Width;
|
|
_requiredHeight = frame.Height;
|
|
|
|
delete[] _frameBuffer;
|
|
_frameBuffer = new uint32_t[frame.Width*frame.Height];
|
|
memset(_frameBuffer, 0, frame.Width * frame.Height *4);
|
|
}
|
|
|
|
memcpy(_frameBuffer, frame.FrameBuffer, frame.Width * frame.Height *_bytesPerPixel);
|
|
_frameChanged = true;
|
|
}
|
|
|
|
bool SdlRenderer::UpdateHudSize(HudRenderInfo& hud, uint32_t width, uint32_t height)
|
|
{
|
|
if(!hud.Texture || hud.Width != width || hud.Height != height) {
|
|
if(hud.Texture) {
|
|
SDL_DestroyTexture(hud.Texture);
|
|
}
|
|
hud.Width = width;
|
|
hud.Height = height;
|
|
hud.Texture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
|
SDL_SetTextureBlendMode(hud.Texture, SDL_BLENDMODE_BLEND);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SdlRenderer::UpdateHudTexture(HudRenderInfo& hud, uint32_t* src)
|
|
{
|
|
uint8_t* textureBuffer;
|
|
int rowPitch;
|
|
if(SDL_LockTexture(hud.Texture, nullptr, (void**)&textureBuffer, &rowPitch) == 0) {
|
|
for(uint32_t i = 0, iMax = hud.Height; i < iMax; i++) {
|
|
memcpy(textureBuffer, src, hud.Width * _bytesPerPixel);
|
|
src += hud.Width;
|
|
textureBuffer += rowPitch;
|
|
}
|
|
} else {
|
|
LogSdlError("SDL_LockTexture failed (HUD)");
|
|
}
|
|
SDL_UnlockTexture(hud.Texture);
|
|
}
|
|
|
|
void SdlRenderer::Render(RenderSurfaceInfo& emuHud, RenderSurfaceInfo& scriptHud)
|
|
{
|
|
SetScreenSize(_requiredWidth, _requiredHeight);
|
|
if(!_sdlRenderer || !_sdlTexture) {
|
|
return;
|
|
}
|
|
|
|
bool needUpdate = false;
|
|
needUpdate |= UpdateHudSize(_emuHud, emuHud.Width, emuHud.Height);
|
|
needUpdate |= UpdateHudSize(_scriptHud, scriptHud.Width, scriptHud.Height);
|
|
|
|
if(SDL_RenderClear(_sdlRenderer) != 0) {
|
|
LogSdlError("SDL_RenderClear failed");
|
|
}
|
|
|
|
uint8_t *textureBuffer;
|
|
int rowPitch;
|
|
if(SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch) == 0) {
|
|
auto frameLock = _frameLock.AcquireSafe();
|
|
if(_frameBuffer && _frameWidth == _requiredWidth && _frameHeight == _requiredHeight) {
|
|
uint32_t* ppuFrameBuffer = _frameBuffer;
|
|
if(rowPitch != _frameWidth) {
|
|
for(uint32_t i = 0, iMax = _frameHeight; i < iMax; i++) {
|
|
memcpy(textureBuffer, ppuFrameBuffer, _frameWidth*_bytesPerPixel);
|
|
ppuFrameBuffer += _frameWidth;
|
|
textureBuffer += rowPitch;
|
|
}
|
|
} else {
|
|
memcpy(textureBuffer, ppuFrameBuffer, _frameHeight * _frameWidth * _bytesPerPixel);
|
|
}
|
|
}
|
|
} else {
|
|
LogSdlError("SDL_LockTexture failed");
|
|
}
|
|
|
|
SDL_UnlockTexture(_sdlTexture);
|
|
|
|
if(needUpdate || emuHud.IsDirty) {
|
|
UpdateHudTexture(_emuHud, emuHud.Buffer);
|
|
}
|
|
if(needUpdate || scriptHud.IsDirty) {
|
|
UpdateHudTexture(_scriptHud, scriptHud.Buffer);
|
|
}
|
|
|
|
SDL_Rect source = {0, 0, (int)_frameWidth, (int)_frameHeight };
|
|
SDL_Rect dest = {0, 0, (int)_screenWidth, (int)_screenHeight };
|
|
|
|
if(SDL_RenderCopy(_sdlRenderer, _sdlTexture, &source, &dest) != 0) {
|
|
LogSdlError("SDL_RenderCopy failed");
|
|
}
|
|
|
|
SDL_Rect scriptHudSource = { 0, 0, (int)_scriptHud.Width, (int)_scriptHud.Height };
|
|
if(SDL_RenderCopy(_sdlRenderer, _scriptHud.Texture, &scriptHudSource, &dest) != 0) {
|
|
LogSdlError("SDL_RenderCopy failed (_scriptHud)");
|
|
}
|
|
|
|
SDL_Rect emuHudSource = { 0, 0, (int)_emuHud.Width, (int)_emuHud.Height };
|
|
if(SDL_RenderCopy(_sdlRenderer, _emuHud.Texture, &emuHudSource, &dest) != 0) {
|
|
LogSdlError("SDL_RenderCopy failed (_emuHud)");
|
|
}
|
|
|
|
SDL_RenderPresent(_sdlRenderer);
|
|
}
|