2013-09-01 14:27:21 +00:00
|
|
|
/*
|
|
|
|
** graphics.cpp
|
|
|
|
**
|
|
|
|
** This file is part of mkxp.
|
|
|
|
**
|
|
|
|
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
|
|
|
**
|
|
|
|
** mkxp 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 2 of the License, or
|
|
|
|
** (at your option) any later version.
|
|
|
|
**
|
|
|
|
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "graphics.h"
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
#include "gl-util.h"
|
2013-10-09 10:30:33 +00:00
|
|
|
#include "sharedstate.h"
|
2013-09-01 14:27:21 +00:00
|
|
|
#include "glstate.h"
|
|
|
|
#include "shader.h"
|
|
|
|
#include "scene.h"
|
|
|
|
#include "quad.h"
|
|
|
|
#include "eventthread.h"
|
|
|
|
#include "texpool.h"
|
|
|
|
#include "bitmap.h"
|
|
|
|
#include "etc-internal.h"
|
|
|
|
#include "binding.h"
|
2013-09-27 14:54:01 +00:00
|
|
|
#include "perftimer.h"
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-12-04 16:48:37 +00:00
|
|
|
#include <SDL_video.h>
|
|
|
|
#include <SDL_timer.h>
|
2013-12-20 08:17:15 +00:00
|
|
|
#include <SDL_image.h>
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-06 12:11:50 +00:00
|
|
|
#include <time.h>
|
2013-12-20 08:17:15 +00:00
|
|
|
#include <sys/time.h>
|
2013-12-08 12:19:22 +00:00
|
|
|
#include <algorithm>
|
2013-10-06 12:11:50 +00:00
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
struct PingPong
|
|
|
|
{
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO rt[2];
|
2013-12-08 12:19:22 +00:00
|
|
|
uint8_t srcInd, dstInd;
|
2013-09-01 14:27:21 +00:00
|
|
|
int screenW, screenH;
|
|
|
|
|
|
|
|
PingPong(int screenW, int screenH)
|
|
|
|
: srcInd(0), dstInd(1),
|
|
|
|
screenW(screenW), screenH(screenH)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
|
|
{
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO::init(rt[i]);
|
|
|
|
TEXFBO::allocEmpty(rt[i], screenW, screenH);
|
|
|
|
TEXFBO::linkFBO(rt[i]);
|
2013-09-01 14:27:21 +00:00
|
|
|
glClearColor(0, 0, 0, 1);
|
2013-10-01 11:10:14 +00:00
|
|
|
FBO::clear();
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~PingPong()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 2; ++i)
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO::fini(rt[i]);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Binds FBO of last good buffer for reading */
|
|
|
|
void bindLastBuffer()
|
|
|
|
{
|
|
|
|
FBO::bind(rt[dstInd].fbo, FBO::Read);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Better not call this during render cycles */
|
|
|
|
void resize(int width, int height)
|
|
|
|
{
|
|
|
|
screenW = width;
|
|
|
|
screenH = height;
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
|
|
{
|
2013-09-06 10:26:41 +00:00
|
|
|
TEX::bind(rt[i].tex);
|
|
|
|
TEX::allocEmpty(width, height);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void startRender()
|
|
|
|
{
|
|
|
|
bind();
|
|
|
|
}
|
|
|
|
|
|
|
|
void swapRender()
|
|
|
|
{
|
2013-12-08 12:19:22 +00:00
|
|
|
std::swap(srcInd, dstInd);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
/* Discard dest buffer */
|
2013-09-06 10:26:41 +00:00
|
|
|
TEX::bind(rt[dstInd].tex);
|
|
|
|
TEX::allocEmpty(screenW, screenH);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
bind();
|
|
|
|
}
|
|
|
|
|
|
|
|
void blitFBOs()
|
|
|
|
{
|
|
|
|
FBO::blit(0, 0, 0, 0, screenW, screenH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void finishRender()
|
|
|
|
{
|
|
|
|
FBO::unbind(FBO::Draw);
|
|
|
|
FBO::bind(rt[dstInd].fbo, FBO::Read);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void bind()
|
|
|
|
{
|
2013-09-23 05:15:01 +00:00
|
|
|
TEX::bind(rt[srcInd].tex);
|
2013-09-01 14:27:21 +00:00
|
|
|
FBO::bind(rt[srcInd].fbo, FBO::Read);
|
|
|
|
FBO::bind(rt[dstInd].fbo, FBO::Draw);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class ScreenScene : public Scene
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ScreenScene(int width, int height)
|
2014-01-01 01:13:02 +00:00
|
|
|
: pp(width, height)
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
|
|
|
updateReso(width, height);
|
2013-10-06 07:55:27 +00:00
|
|
|
|
|
|
|
#ifdef RGSS2
|
|
|
|
brightEffect = false;
|
2013-09-01 14:27:21 +00:00
|
|
|
brightnessQuad.setColor(Vec4());
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void composite()
|
|
|
|
{
|
|
|
|
const int w = geometry.rect.w;
|
|
|
|
const int h = geometry.rect.h;
|
|
|
|
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->prepareDraw();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
pp.startRender();
|
|
|
|
|
2013-09-23 05:15:01 +00:00
|
|
|
glState.viewport.set(IntRect(0, 0, w, h));
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-01 11:10:14 +00:00
|
|
|
FBO::clear();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
Scene::composite();
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
if (brightEffect)
|
|
|
|
{
|
2013-12-11 04:22:13 +00:00
|
|
|
SimpleColorShader &shader = shState->shaders().simpleColor;
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.bind();
|
|
|
|
shader.applyViewportProj();
|
|
|
|
shader.setTranslation(Vec2i());
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
brightnessQuad.draw();
|
|
|
|
}
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
pp.finishRender();
|
|
|
|
}
|
|
|
|
|
|
|
|
void requestViewportRender(Vec4 &c, Vec4 &f, Vec4 &t)
|
|
|
|
{
|
|
|
|
pp.swapRender();
|
2013-12-08 11:23:12 +00:00
|
|
|
|
|
|
|
/* Scissor test _does_ affect FBO blit operations,
|
|
|
|
* and since we're inside the draw cycle, it will
|
|
|
|
* be turned on, so turn it off temporarily */
|
|
|
|
glState.scissorTest.pushSet(false);
|
2013-09-01 14:27:21 +00:00
|
|
|
pp.blitFBOs();
|
2013-12-08 11:23:12 +00:00
|
|
|
glState.scissorTest.pop();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-12-11 04:22:13 +00:00
|
|
|
PlaneShader &shader = shState->shaders().plane;
|
2013-09-01 14:27:21 +00:00
|
|
|
shader.bind();
|
|
|
|
shader.setColor(c);
|
|
|
|
shader.setFlash(f);
|
|
|
|
shader.setTone(t);
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.applyViewportProj();
|
|
|
|
shader.setTexSize(geometry.rect.size());
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
glState.blendMode.pushSet(BlendNone);
|
|
|
|
|
|
|
|
screenQuad.draw();
|
|
|
|
|
|
|
|
glState.blendMode.pop();
|
|
|
|
shader.unbind();
|
|
|
|
}
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
void setBrightness(float norm)
|
|
|
|
{
|
|
|
|
brightnessQuad.setColor(Vec4(0, 0, 0, 1.0 - norm));
|
|
|
|
|
|
|
|
brightEffect = norm < 1.0;
|
|
|
|
}
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
void updateReso(int width, int height)
|
|
|
|
{
|
|
|
|
geometry.rect.w = width;
|
|
|
|
geometry.rect.h = height;
|
|
|
|
|
|
|
|
screenQuad.setTexPosRect(geometry.rect, geometry.rect);
|
2013-10-06 07:55:27 +00:00
|
|
|
|
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
brightnessQuad.setTexPosRect(geometry.rect, geometry.rect);
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
notifyGeometryChange();
|
|
|
|
}
|
|
|
|
|
|
|
|
void setResolution(int width, int height)
|
|
|
|
{
|
|
|
|
pp.resize(width, height);
|
|
|
|
updateReso(width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
PingPong &getPP()
|
|
|
|
{
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
PingPong pp;
|
|
|
|
Quad screenQuad;
|
2013-10-06 07:55:27 +00:00
|
|
|
|
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
Quad brightnessQuad;
|
|
|
|
bool brightEffect;
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FPSLimiter
|
|
|
|
{
|
2013-10-06 12:11:50 +00:00
|
|
|
uint64_t lastTickCount;
|
|
|
|
|
|
|
|
/* ticks per frame */
|
|
|
|
int64_t tpf;
|
|
|
|
|
|
|
|
/* Ticks per second */
|
|
|
|
const uint64_t tickFreq;
|
|
|
|
|
|
|
|
/* Ticks per milisecond */
|
|
|
|
const uint64_t tickFreqMS;
|
|
|
|
|
|
|
|
/* Ticks per nanosecond */
|
|
|
|
const double tickFreqNS;
|
|
|
|
|
|
|
|
/* Data for frame timing adjustment */
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
/* Last tick count */
|
|
|
|
uint64_t last;
|
|
|
|
|
|
|
|
/* How far behind/in front we are for ideal frame timing */
|
|
|
|
int64_t idealDiff;
|
|
|
|
|
|
|
|
bool resetFlag;
|
|
|
|
} adj;
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-06 12:11:50 +00:00
|
|
|
FPSLimiter(uint16_t desiredFPS)
|
|
|
|
: lastTickCount(SDL_GetPerformanceCounter()),
|
|
|
|
tickFreq(SDL_GetPerformanceFrequency()),
|
|
|
|
tickFreqMS(tickFreq / 1000),
|
|
|
|
tickFreqNS(tickFreq / 1000000000)
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
|
|
|
setDesiredFPS(desiredFPS);
|
2013-10-06 12:11:50 +00:00
|
|
|
|
|
|
|
adj.last = SDL_GetPerformanceCounter();
|
|
|
|
adj.idealDiff = 0;
|
|
|
|
adj.resetFlag = false;
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-10-06 12:11:50 +00:00
|
|
|
void setDesiredFPS(uint16_t value)
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
2013-10-06 12:11:50 +00:00
|
|
|
tpf = tickFreq / value;
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void delay()
|
|
|
|
{
|
2013-10-06 12:11:50 +00:00
|
|
|
int64_t tickDelta = SDL_GetPerformanceCounter() - lastTickCount;
|
|
|
|
int64_t toDelay = tpf - tickDelta;
|
|
|
|
|
|
|
|
/* Compensate for the last delta
|
|
|
|
* to the ideal timestep */
|
|
|
|
toDelay -= adj.idealDiff;
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
if (toDelay < 0)
|
|
|
|
toDelay = 0;
|
|
|
|
|
2013-10-06 12:11:50 +00:00
|
|
|
delayTicks(toDelay);
|
|
|
|
|
|
|
|
uint64_t now = lastTickCount = SDL_GetPerformanceCounter();
|
|
|
|
int64_t diff = now - adj.last;
|
|
|
|
adj.last = now;
|
|
|
|
|
|
|
|
/* Recalculate our temporal position
|
|
|
|
* relative to the ideal timestep */
|
|
|
|
adj.idealDiff = diff - tpf + adj.idealDiff;
|
|
|
|
|
|
|
|
if (adj.resetFlag)
|
|
|
|
{
|
|
|
|
adj.idealDiff = 0;
|
|
|
|
adj.resetFlag = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void resetFrameAdjust()
|
|
|
|
{
|
|
|
|
adj.resetFlag = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we're more than a full frame's worth
|
|
|
|
* of ticks behind the ideal timestep,
|
|
|
|
* there's no choice but to skip frame(s)
|
|
|
|
* to catch up */
|
|
|
|
bool frameSkipRequired() const
|
|
|
|
{
|
|
|
|
return adj.idealDiff > tpf;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void delayTicks(uint64_t ticks)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_NANOSLEEP
|
|
|
|
struct timespec req;
|
|
|
|
req.tv_sec = 0;
|
|
|
|
req.tv_nsec = ticks / tickFreqNS;
|
|
|
|
while (nanosleep(&req, &req) == -1)
|
|
|
|
;
|
|
|
|
#else
|
|
|
|
SDL_Delay(ticks / tickFreqMS);
|
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct GraphicsPrivate
|
|
|
|
{
|
2013-09-23 08:39:16 +00:00
|
|
|
/* Screen resolution, ie. the resolution at which
|
2013-09-23 09:18:20 +00:00
|
|
|
* RGSS renders at (settable with Graphics.resize_screen).
|
2013-09-23 08:39:16 +00:00
|
|
|
* Can only be changed from within RGSS */
|
2013-09-01 14:27:21 +00:00
|
|
|
Vec2i scRes;
|
2013-09-23 08:39:16 +00:00
|
|
|
|
|
|
|
/* Screen size, to which the rendered frames are scaled up.
|
|
|
|
* This can be smaller than the window size when fixed aspect
|
|
|
|
* ratio is enforced */
|
2013-09-01 14:27:21 +00:00
|
|
|
Vec2i scSize;
|
|
|
|
|
2013-09-23 08:39:16 +00:00
|
|
|
/* Actual physical size of the game window */
|
|
|
|
Vec2i winSize;
|
|
|
|
|
|
|
|
/* Offset in the game window at which the scaled game screen
|
|
|
|
* is blitted inside the game window */
|
|
|
|
Vec2i scOffset;
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
ScreenScene screen;
|
|
|
|
RGSSThreadData *threadData;
|
|
|
|
|
|
|
|
int frameRate;
|
|
|
|
int frameCount;
|
2013-10-06 07:55:27 +00:00
|
|
|
|
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
int brightness;
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
FPSLimiter fpsLimiter;
|
|
|
|
|
2013-09-27 14:54:01 +00:00
|
|
|
PerfTimer *gpuTimer;
|
|
|
|
PerfTimer *cpuTimer;
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
bool frozen;
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO frozenScene;
|
|
|
|
TEXFBO currentScene;
|
2013-09-01 14:27:21 +00:00
|
|
|
Quad screenQuad;
|
2013-09-06 10:16:24 +00:00
|
|
|
RBOFBO transBuffer;
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
GraphicsPrivate()
|
|
|
|
: scRes(640, 480),
|
|
|
|
scSize(scRes),
|
2013-09-23 08:39:16 +00:00
|
|
|
winSize(scRes),
|
2013-09-01 14:27:21 +00:00
|
|
|
screen(scRes.x, scRes.y),
|
|
|
|
frameRate(40),
|
|
|
|
frameCount(0),
|
2013-10-06 07:55:27 +00:00
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
brightness(255),
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
fpsLimiter(frameRate),
|
|
|
|
frozen(false)
|
|
|
|
{
|
2013-09-27 14:54:01 +00:00
|
|
|
gpuTimer = createGPUTimer(frameRate);
|
|
|
|
cpuTimer = createCPUTimer(frameRate);
|
|
|
|
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO::init(frozenScene);
|
|
|
|
TEXFBO::allocEmpty(frozenScene, scRes.x, scRes.y);
|
|
|
|
TEXFBO::linkFBO(frozenScene);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO::init(currentScene);
|
|
|
|
TEXFBO::allocEmpty(currentScene, scRes.x, scRes.y);
|
|
|
|
TEXFBO::linkFBO(currentScene);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
FloatRect screenRect(0, 0, scRes.x, scRes.y);
|
|
|
|
screenQuad.setTexPosRect(screenRect, screenRect);
|
|
|
|
|
2013-09-06 10:16:24 +00:00
|
|
|
RBOFBO::init(transBuffer);
|
|
|
|
RBOFBO::allocEmpty(transBuffer, scRes.x, scRes.y);
|
|
|
|
RBOFBO::linkFBO(transBuffer);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~GraphicsPrivate()
|
|
|
|
{
|
2013-09-27 14:54:01 +00:00
|
|
|
delete gpuTimer;
|
|
|
|
delete cpuTimer;
|
|
|
|
|
2013-09-06 10:26:41 +00:00
|
|
|
TEXFBO::fini(frozenScene);
|
|
|
|
TEXFBO::fini(currentScene);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-09-06 10:16:24 +00:00
|
|
|
RBOFBO::fini(transBuffer);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void updateScreenResoRatio()
|
|
|
|
{
|
2013-10-09 10:30:33 +00:00
|
|
|
Vec2 &ratio = shState->rtData().sizeResoRatio;
|
2013-09-01 14:27:21 +00:00
|
|
|
ratio.x = (float) scRes.x / scSize.x;
|
|
|
|
ratio.y = (float) scRes.y / scSize.y;
|
2013-09-23 08:39:16 +00:00
|
|
|
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->rtData().screenOffset = scOffset;
|
2013-09-23 08:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enforces fixed aspect ratio, if desired */
|
|
|
|
void recalculateScreenSize()
|
|
|
|
{
|
|
|
|
scSize = winSize;
|
|
|
|
|
2013-09-23 09:00:50 +00:00
|
|
|
if (!threadData->config.fixedAspectRatio)
|
2013-09-23 08:39:16 +00:00
|
|
|
{
|
|
|
|
scOffset = Vec2i(0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float resRatio = (float) scRes.x / scRes.y;
|
|
|
|
float winRatio = (float) winSize.x / winSize.y;
|
|
|
|
|
|
|
|
if (resRatio > winRatio)
|
|
|
|
scSize.y = scSize.x / resRatio;
|
|
|
|
else if (resRatio < winRatio)
|
|
|
|
scSize.x = scSize.y * resRatio;
|
|
|
|
|
|
|
|
scOffset.x = (winSize.x - scSize.x) / 2.f;
|
|
|
|
scOffset.y = (winSize.y - scSize.y) / 2.f;
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void checkResize()
|
|
|
|
{
|
2013-09-23 08:39:16 +00:00
|
|
|
if (threadData->windowSizeMsg.pollChange(&winSize.x, &winSize.y))
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
2014-01-04 12:49:08 +00:00
|
|
|
/* some GL drivers change the viewport on window resize */
|
2014-01-01 00:56:20 +00:00
|
|
|
glState.viewport.refresh();
|
2013-09-23 08:39:16 +00:00
|
|
|
recalculateScreenSize();
|
2013-09-01 14:27:21 +00:00
|
|
|
updateScreenResoRatio();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shutdown()
|
|
|
|
{
|
|
|
|
threadData->rqTermAck = true;
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->texPool().disable();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
scriptBinding->terminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void swapGLBuffer()
|
|
|
|
{
|
|
|
|
fpsLimiter.delay();
|
2013-10-06 12:11:50 +00:00
|
|
|
SDL_GL_SwapWindow(threadData->window);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
++frameCount;
|
2013-10-06 05:05:01 +00:00
|
|
|
|
|
|
|
threadData->ethread->notifyFrame();
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void compositeToBuffer(FBO::ID fbo)
|
|
|
|
{
|
|
|
|
screen.composite();
|
|
|
|
FBO::bind(fbo, FBO::Draw);
|
|
|
|
FBO::blit(0, 0, 0, 0, scRes.x, scRes.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void blitBufferFlippedScaled()
|
|
|
|
{
|
2013-09-23 08:39:16 +00:00
|
|
|
FBO::blit(0, 0, scRes.x, scRes.y,
|
2013-09-23 09:00:50 +00:00
|
|
|
scOffset.x, scSize.y+scOffset.y, scSize.x, -scSize.y,
|
|
|
|
threadData->config.smoothScaling ? FBO::Linear : FBO::Nearest);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void redrawScreen()
|
|
|
|
{
|
|
|
|
screen.composite();
|
2013-12-08 11:57:46 +00:00
|
|
|
FBO::clear();
|
|
|
|
blitBufferFlippedScaled();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
swapGLBuffer();
|
|
|
|
}
|
2013-12-20 08:17:15 +00:00
|
|
|
|
|
|
|
void writeScreenshot(const char *filename)
|
|
|
|
{
|
|
|
|
int bpp;
|
|
|
|
uint32_t rm, gm, bm, am;
|
|
|
|
SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rm, &gm, &bm, &am);
|
|
|
|
|
|
|
|
/* Discard alpha channel because it might have bogus values */
|
|
|
|
SDL_Surface *screenshot =
|
|
|
|
SDL_CreateRGBSurface(0, scRes.x, scRes.y, bpp, rm, gm, bm, 0);
|
|
|
|
|
|
|
|
screen.getPP().bindLastBuffer();
|
|
|
|
|
2014-01-03 17:33:44 +00:00
|
|
|
glReadPixels(0, 0, scRes.x, scRes.y, GL_RGBA, GL_UNSIGNED_BYTE, screenshot->pixels);
|
2013-12-20 08:17:15 +00:00
|
|
|
|
|
|
|
IMG_SavePNG(screenshot, filename);
|
|
|
|
|
|
|
|
SDL_FreeSurface(screenshot);
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkScreenshotRq()
|
|
|
|
{
|
|
|
|
if (!threadData->rqScreenshot)
|
|
|
|
return;
|
|
|
|
|
|
|
|
threadData->rqScreenshot = false;
|
|
|
|
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, 0);
|
|
|
|
struct tm tm = *localtime(&tv.tv_sec);
|
|
|
|
|
|
|
|
char filename[32];
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "%d%02d%02d-%02d%02d%02d.png",
|
|
|
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
|
|
|
|
|
|
writeScreenshot(filename);
|
|
|
|
}
|
2013-09-01 14:27:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Graphics::Graphics(RGSSThreadData *data)
|
|
|
|
{
|
|
|
|
p = new GraphicsPrivate;
|
|
|
|
p->threadData = data;
|
2013-10-17 00:18:16 +00:00
|
|
|
|
|
|
|
if (data->config.fixedFramerate > 0)
|
|
|
|
p->fpsLimiter.setDesiredFPS(data->config.fixedFramerate);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Graphics::~Graphics()
|
|
|
|
{
|
|
|
|
delete p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::update()
|
|
|
|
{
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->checkShutdown();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-09-27 14:54:01 +00:00
|
|
|
// p->cpuTimer->endTiming();
|
|
|
|
// p->gpuTimer->startTiming();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-12-20 08:17:15 +00:00
|
|
|
p->checkScreenshotRq();
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
if (p->frozen)
|
|
|
|
return;
|
|
|
|
|
2013-10-06 12:11:50 +00:00
|
|
|
if (p->fpsLimiter.frameSkipRequired())
|
|
|
|
{
|
2013-12-21 20:18:20 +00:00
|
|
|
if (p->threadData->config.frameSkip)
|
|
|
|
{
|
|
|
|
/* Skip frame */
|
|
|
|
p->fpsLimiter.delay();
|
|
|
|
++p->frameCount;
|
|
|
|
p->threadData->ethread->notifyFrame();
|
2013-10-06 12:11:50 +00:00
|
|
|
|
2013-12-21 20:18:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Just reset frame adjust counter */
|
|
|
|
p->fpsLimiter.resetFrameAdjust();
|
|
|
|
}
|
2013-10-06 12:11:50 +00:00
|
|
|
}
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
p->checkResize();
|
|
|
|
p->redrawScreen();
|
|
|
|
|
2013-09-27 14:54:01 +00:00
|
|
|
// p->gpuTimer->endTiming();
|
|
|
|
// p->cpuTimer->startTiming();
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::freeze()
|
|
|
|
{
|
|
|
|
p->frozen = true;
|
|
|
|
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->checkShutdown();
|
2013-09-01 14:27:21 +00:00
|
|
|
p->checkResize();
|
|
|
|
|
|
|
|
/* Capture scene into frozen buffer */
|
|
|
|
p->compositeToBuffer(p->frozenScene.fbo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::transition(int duration,
|
|
|
|
const char *filename,
|
|
|
|
int vague)
|
|
|
|
{
|
2013-09-03 13:31:29 +00:00
|
|
|
vague = clamp(vague, 0, 512);
|
2013-09-01 14:27:21 +00:00
|
|
|
Bitmap *transMap = filename ? new Bitmap(filename) : 0;
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#ifdef RGSS2
|
2013-09-01 14:27:21 +00:00
|
|
|
setBrightness(255);
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
/* Capture new scene */
|
|
|
|
p->compositeToBuffer(p->currentScene.fbo);
|
|
|
|
|
2013-09-02 15:31:34 +00:00
|
|
|
/* If no transition bitmap is provided,
|
|
|
|
* we can use a simplified shader */
|
2013-09-01 14:27:21 +00:00
|
|
|
if (transMap)
|
|
|
|
{
|
2013-12-11 04:22:13 +00:00
|
|
|
TransShader &shader = shState->shaders().trans;
|
2013-09-01 14:27:21 +00:00
|
|
|
shader.bind();
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.applyViewportProj();
|
2013-09-01 14:27:21 +00:00
|
|
|
shader.setFrozenScene(p->frozenScene.tex);
|
|
|
|
shader.setCurrentScene(p->currentScene.tex);
|
|
|
|
shader.setTransMap(transMap->getGLTypes().tex);
|
|
|
|
shader.setVague(vague / 512.0f);
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.setTexSize(p->scRes);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-11 04:22:13 +00:00
|
|
|
SimpleTransShader &shader = shState->shaders().simpleTrans;
|
2013-09-01 14:27:21 +00:00
|
|
|
shader.bind();
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.applyViewportProj();
|
2013-09-01 14:27:21 +00:00
|
|
|
shader.setFrozenScene(p->frozenScene.tex);
|
|
|
|
shader.setCurrentScene(p->currentScene.tex);
|
2013-09-23 05:15:01 +00:00
|
|
|
shader.setTexSize(p->scRes);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
glState.blendMode.pushSet(BlendNone);
|
|
|
|
|
|
|
|
for (int i = 0; i < duration; ++i)
|
|
|
|
{
|
|
|
|
if (p->threadData->rqTerm)
|
|
|
|
{
|
|
|
|
delete transMap;
|
|
|
|
p->shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
const float prog = i * (1.0 / duration);
|
|
|
|
|
|
|
|
if (transMap)
|
2013-12-11 04:22:13 +00:00
|
|
|
shState->shaders().trans.setProg(prog);
|
2013-09-01 14:27:21 +00:00
|
|
|
else
|
2013-12-11 04:22:13 +00:00
|
|
|
shState->shaders().simpleTrans.setProg(prog);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-09-02 15:31:34 +00:00
|
|
|
/* Draw the composed frame to a buffer first
|
|
|
|
* (we need this because we're skipping PingPong) */
|
2013-09-06 12:56:30 +00:00
|
|
|
FBO::bind(p->transBuffer.fbo, FBO::Draw);
|
2013-10-01 11:10:14 +00:00
|
|
|
FBO::clear();
|
2013-09-01 14:27:21 +00:00
|
|
|
p->screenQuad.draw();
|
|
|
|
|
|
|
|
p->checkResize();
|
|
|
|
|
2013-09-02 15:31:34 +00:00
|
|
|
/* Then blit it flipped and scaled to the screen */
|
2013-09-01 14:27:21 +00:00
|
|
|
FBO::bind(p->transBuffer.fbo, FBO::Read);
|
2013-12-08 11:57:46 +00:00
|
|
|
FBO::unbind(FBO::Draw);
|
|
|
|
FBO::clear();
|
|
|
|
p->blitBufferFlippedScaled();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
p->swapGLBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
glState.blendMode.pop();
|
|
|
|
|
|
|
|
delete transMap;
|
|
|
|
|
|
|
|
p->frozen = false;
|
|
|
|
}
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
void Graphics::frameReset()
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
2013-10-06 12:11:50 +00:00
|
|
|
p->fpsLimiter.resetFrameAdjust();
|
2013-10-06 07:55:27 +00:00
|
|
|
}
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#undef RET_IF_DISP
|
|
|
|
#define RET_IF_DISP(x)
|
|
|
|
|
|
|
|
#undef CHK_DISP
|
|
|
|
#define CHK_DISP
|
|
|
|
|
|
|
|
DEF_ATTR_RD_SIMPLE(Graphics, FrameRate, int, p->frameRate)
|
|
|
|
|
|
|
|
DEF_ATTR_SIMPLE(Graphics, FrameCount, int, p->frameCount)
|
|
|
|
|
|
|
|
void Graphics::setFrameRate(int value)
|
|
|
|
{
|
|
|
|
p->frameRate = clamp(value, 10, 120);
|
2013-10-17 00:18:16 +00:00
|
|
|
|
|
|
|
if (p->threadData->config.fixedFramerate > 0)
|
|
|
|
return;
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
p->fpsLimiter.setDesiredFPS(p->frameRate);
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#ifdef RGSS2
|
|
|
|
|
|
|
|
void Graphics::wait(int duration)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < duration; ++i)
|
|
|
|
{
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->checkShutdown();
|
2013-10-06 07:55:27 +00:00
|
|
|
p->checkResize();
|
|
|
|
p->redrawScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::fadeout(int duration)
|
|
|
|
{
|
|
|
|
if (p->frozen)
|
|
|
|
FBO::bind(p->frozenScene.fbo, FBO::Read);
|
|
|
|
|
2013-12-11 20:07:41 +00:00
|
|
|
FBO::unbind(FBO::Draw);
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
for (int i = duration-1; i > -1; --i)
|
|
|
|
{
|
|
|
|
setBrightness((255.0 / duration) * i);
|
|
|
|
|
|
|
|
if (p->frozen)
|
|
|
|
{
|
2013-12-11 20:07:41 +00:00
|
|
|
FBO::clear();
|
|
|
|
p->blitBufferFlippedScaled();
|
2013-10-06 07:55:27 +00:00
|
|
|
p->swapGLBuffer();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::fadein(int duration)
|
|
|
|
{
|
|
|
|
if (p->frozen)
|
|
|
|
FBO::bind(p->frozenScene.fbo, FBO::Read);
|
|
|
|
|
2013-12-11 20:07:41 +00:00
|
|
|
FBO::unbind(FBO::Draw);
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
for (int i = 0; i < duration; ++i)
|
|
|
|
{
|
|
|
|
setBrightness((255.0 / duration) * i);
|
|
|
|
|
|
|
|
if (p->frozen)
|
|
|
|
{
|
2013-12-11 20:07:41 +00:00
|
|
|
FBO::clear();
|
|
|
|
p->blitBufferFlippedScaled();
|
2013-10-06 07:55:27 +00:00
|
|
|
p->swapGLBuffer();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Bitmap *Graphics::snapToBitmap()
|
2013-09-01 14:27:21 +00:00
|
|
|
{
|
2013-10-06 07:55:27 +00:00
|
|
|
Bitmap *bitmap = new Bitmap(width(), height());
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
p->compositeToBuffer(bitmap->getGLTypes().fbo);
|
|
|
|
|
|
|
|
return bitmap;
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int Graphics::width() const
|
|
|
|
{
|
|
|
|
return p->scRes.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Graphics::height() const
|
|
|
|
{
|
|
|
|
return p->scRes.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::resizeScreen(int width, int height)
|
|
|
|
{
|
2013-09-03 13:31:29 +00:00
|
|
|
width = clamp(width, 1, 640);
|
|
|
|
height = clamp(height, 1, 480);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
Vec2i size(width, height);
|
|
|
|
|
|
|
|
if (p->scRes == size)
|
|
|
|
return;
|
|
|
|
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->eThread().requestWindowResize(width, height);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
p->scRes = size;
|
|
|
|
|
|
|
|
p->screen.setResolution(width, height);
|
|
|
|
|
2013-09-06 10:26:41 +00:00
|
|
|
TEX::bind(p->frozenScene.tex);
|
|
|
|
TEX::allocEmpty(width, height);
|
|
|
|
TEX::bind(p->currentScene.tex);
|
|
|
|
TEX::allocEmpty(width, height);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
FloatRect screenRect(0, 0, width, height);
|
|
|
|
p->screenQuad.setTexPosRect(screenRect, screenRect);
|
|
|
|
|
2013-09-06 10:16:24 +00:00
|
|
|
RBO::bind(p->transBuffer.rbo);
|
|
|
|
RBO::allocEmpty(width, height);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
p->updateScreenResoRatio();
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_ATTR_RD_SIMPLE(Graphics, Brightness, int, p->brightness)
|
|
|
|
|
|
|
|
void Graphics::setBrightness(int value)
|
|
|
|
{
|
2013-09-03 13:31:29 +00:00
|
|
|
value = clamp(value, 0, 255);
|
2013-09-01 14:27:21 +00:00
|
|
|
|
|
|
|
if (p->brightness == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
p->brightness = value;
|
|
|
|
p->screen.setBrightness(value / 255.0);
|
|
|
|
}
|
|
|
|
|
2013-10-06 07:55:27 +00:00
|
|
|
#endif
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
bool Graphics::getFullscreen() const
|
|
|
|
{
|
|
|
|
return p->threadData->ethread->getFullscreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::setFullscreen(bool value)
|
|
|
|
{
|
|
|
|
p->threadData->ethread->requestFullscreenMode(value);
|
|
|
|
}
|
|
|
|
|
2013-09-24 20:52:42 +00:00
|
|
|
bool Graphics::getShowCursor() const
|
|
|
|
{
|
|
|
|
return p->threadData->ethread->getShowCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::setShowCursor(bool value)
|
|
|
|
{
|
|
|
|
p->threadData->ethread->requestShowCursor(value);
|
|
|
|
}
|
|
|
|
|
2013-09-01 14:27:21 +00:00
|
|
|
Scene *Graphics::getScreen() const
|
|
|
|
{
|
|
|
|
return &p->screen;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Graphics::repaintWait(volatile bool *exitCond)
|
|
|
|
{
|
|
|
|
if (*exitCond)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Repaint the screen with the last good frame we drew */
|
|
|
|
p->screen.getPP().bindLastBuffer();
|
|
|
|
FBO::unbind(FBO::Draw);
|
|
|
|
|
|
|
|
while (!*exitCond)
|
|
|
|
{
|
2013-10-09 10:30:33 +00:00
|
|
|
shState->checkShutdown();
|
2013-09-01 14:27:21 +00:00
|
|
|
|
2013-10-01 11:10:14 +00:00
|
|
|
FBO::clear();
|
2013-09-01 14:27:21 +00:00
|
|
|
p->blitBufferFlippedScaled();
|
|
|
|
SDL_GL_SwapWindow(p->threadData->window);
|
|
|
|
p->fpsLimiter.delay();
|
2013-10-06 05:05:01 +00:00
|
|
|
|
|
|
|
p->threadData->ethread->notifyFrame();
|
2013-09-01 14:27:21 +00:00
|
|
|
}
|
|
|
|
}
|