Files

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;
}