mirror of
https://github.com/libretro/Gearboy.git
synced 2026-11-09 11:51:27 -10:00
268 lines
7.2 KiB
C++
268 lines
7.2 KiB
C++
/*
|
|
* Gearboy - Nintendo Game Boy Emulator
|
|
* Copyright (C) 2012 Ignacio Sanchez
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see http://www.gnu.org/licenses/
|
|
*
|
|
*/
|
|
|
|
#include <QMainWindow>
|
|
#include "RenderThread.h"
|
|
#include "GLFrame.h"
|
|
#include "Emulator.h"
|
|
|
|
const float kMixFrameAlpha = 0.35f;
|
|
|
|
RenderThread::RenderThread(GLFrame* pGLFrame) : QThread(), m_pGLFrame(pGLFrame)
|
|
{
|
|
m_bPaused = false;
|
|
m_bDoRendering = true;
|
|
m_pFrameBuffer = new GB_Color[GAMEBOY_WIDTH * GAMEBOY_HEIGHT];
|
|
m_iWidth = 0;
|
|
m_iHeight = 0;
|
|
InitPointer(m_pEmulator);
|
|
m_bFiltering = false;
|
|
m_bMixFrames = true;
|
|
m_bResizeEvent = false;
|
|
m_AccumulationFramebuffer = 0;
|
|
m_AccumulationTexture = 0;
|
|
m_GBTexture = 0;
|
|
m_bFirstFrame = true;
|
|
}
|
|
|
|
RenderThread::~RenderThread()
|
|
{
|
|
}
|
|
|
|
void RenderThread::ResizeViewport(const QSize &size, int pixel_ratio)
|
|
{
|
|
m_iWidth = size.width() * pixel_ratio;
|
|
m_iHeight = size.height() * pixel_ratio;
|
|
m_bResizeEvent = true;
|
|
}
|
|
|
|
void RenderThread::Stop()
|
|
{
|
|
m_bDoRendering = false;
|
|
}
|
|
|
|
void RenderThread::Pause()
|
|
{
|
|
m_bPaused = true;
|
|
}
|
|
|
|
void RenderThread::Resume()
|
|
{
|
|
m_bPaused = false;
|
|
}
|
|
|
|
bool RenderThread::IsRunningEmulator()
|
|
{
|
|
return m_bDoRendering;
|
|
}
|
|
|
|
void RenderThread::SetEmulator(Emulator* pEmulator)
|
|
{
|
|
m_pEmulator = pEmulator;
|
|
}
|
|
|
|
void RenderThread::run()
|
|
{
|
|
m_pGLFrame->makeCurrent();
|
|
|
|
Init();
|
|
|
|
while (m_bDoRendering)
|
|
{
|
|
if (m_pGLFrame->parentWidget()->window()->isVisible())
|
|
{
|
|
|
|
m_pGLFrame->makeCurrent();
|
|
|
|
if (m_bPaused)
|
|
{
|
|
msleep(200);
|
|
}
|
|
else
|
|
{
|
|
m_pEmulator->RunToVBlank(m_pFrameBuffer);
|
|
|
|
if (m_bResizeEvent)
|
|
{
|
|
m_bResizeEvent = false;
|
|
m_bFirstFrame = true;
|
|
}
|
|
|
|
if (m_bMixFrames && !m_pEmulator->IsCGBRom())
|
|
RenderMixFrames();
|
|
else
|
|
RenderFrame();
|
|
}
|
|
|
|
m_pGLFrame->swapBuffers();
|
|
}
|
|
}
|
|
|
|
SafeDeleteArray(m_pFrameBuffer);
|
|
glDeleteTextures(1, &m_AccumulationTexture);
|
|
glDeleteTextures(1, &m_GBTexture);
|
|
glDeleteFramebuffers(1, &m_AccumulationFramebuffer);
|
|
}
|
|
|
|
void RenderThread::Init()
|
|
{
|
|
m_bFirstFrame = true;
|
|
|
|
for (int y = 0; y < GAMEBOY_HEIGHT; ++y)
|
|
{
|
|
for (int x = 0; x < GAMEBOY_WIDTH; ++x)
|
|
{
|
|
int pixel = (y * GAMEBOY_WIDTH) + x;
|
|
m_pFrameBuffer[pixel].red = m_pFrameBuffer[pixel].green =
|
|
m_pFrameBuffer[pixel].blue = 0x00;
|
|
m_pFrameBuffer[pixel].alpha = 0xFF;
|
|
}
|
|
}
|
|
|
|
#ifndef __APPLE__
|
|
GLenum err = glewInit();
|
|
if (GLEW_OK != err)
|
|
{
|
|
/* Problem: glewInit failed, something is seriously wrong. */
|
|
Log("GLEW Error: %s\n", glewGetErrorString(err));
|
|
}
|
|
Log("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
|
|
#endif
|
|
|
|
glGenFramebuffers(1, &m_AccumulationFramebuffer);
|
|
glGenTextures(1, &m_AccumulationTexture);
|
|
glGenTextures(1, &m_GBTexture);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, m_GBTexture);
|
|
SetupTexture((GLvoid*) m_pFrameBuffer);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_AccumulationFramebuffer);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, m_AccumulationTexture);
|
|
SetupTexture(NULL);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
m_AccumulationTexture, 0);
|
|
}
|
|
|
|
void RenderThread::SetupTexture(GLvoid* data)
|
|
{
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GAMEBOY_WIDTH, GAMEBOY_HEIGHT, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) data);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
}
|
|
|
|
void RenderThread::RenderFrame()
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glBindTexture(GL_TEXTURE_2D, m_GBTexture);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GAMEBOY_WIDTH, GAMEBOY_HEIGHT,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) m_pFrameBuffer);
|
|
if (m_bFiltering)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
RenderQuad(m_iWidth, m_iHeight, false);
|
|
}
|
|
|
|
void RenderThread::RenderMixFrames()
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_AccumulationFramebuffer);
|
|
glBindTexture(GL_TEXTURE_2D, m_GBTexture);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GAMEBOY_WIDTH, GAMEBOY_HEIGHT,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) m_pFrameBuffer);
|
|
|
|
float alpha = kMixFrameAlpha;
|
|
if (m_bFirstFrame)
|
|
{
|
|
m_bFirstFrame = false;
|
|
alpha = 1.0f;
|
|
}
|
|
|
|
static bool round_error = false;
|
|
float round_color = 1.0f - (round_error ? 0.03f : 0.0f);
|
|
round_error = !round_error;
|
|
glEnable(GL_BLEND);
|
|
glColor4f(round_color, round_color, round_color, alpha);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
RenderQuad(GAMEBOY_WIDTH, GAMEBOY_HEIGHT, false);
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
glDisable(GL_BLEND);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glBindTexture(GL_TEXTURE_2D, m_AccumulationTexture);
|
|
if (m_bFiltering)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
RenderQuad(m_iWidth, m_iHeight, true);
|
|
}
|
|
|
|
void RenderThread::RenderQuad(int viewportWidth, int viewportHeight, bool mirrorY)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
if (mirrorY)
|
|
glOrtho(0, viewportWidth, 0, viewportHeight, -1, 1);
|
|
else
|
|
glOrtho(0, viewportWidth, viewportHeight, 0, -1, 1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glViewport(0, 0, viewportWidth, viewportHeight);
|
|
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2d(0.0, 0.0);
|
|
glVertex2d(0.0, 0.0);
|
|
glTexCoord2d(1.0, 0.0);
|
|
glVertex2d(viewportWidth, 0.0);
|
|
glTexCoord2d(1.0, 1.0);
|
|
glVertex2d(viewportWidth, viewportHeight);
|
|
glTexCoord2d(0.0, 1.0);
|
|
glVertex2d(0.0, viewportHeight);
|
|
glEnd();
|
|
}
|
|
|
|
void RenderThread::SetBilinearFiletering(bool enabled)
|
|
{
|
|
m_bFiltering = enabled;
|
|
}
|
|
|
|
void RenderThread::SetMixFrames(bool enabled)
|
|
{
|
|
m_bMixFrames = enabled;
|
|
}
|
|
|