2013-04-18 03:29:41 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2014-05-03 02:47:04 +00:00
|
|
|
#include <algorithm>
|
2014-06-03 05:08:54 +00:00
|
|
|
#include <string>
|
2014-05-03 02:47:04 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Common/Common.h"
|
2014-06-03 05:08:54 +00:00
|
|
|
#include "Common/StringUtil.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/Core.h"
|
|
|
|
#include "VideoBackends/OGL/GLUtil.h"
|
|
|
|
#include "VideoBackends/Software/RasterFont.h"
|
|
|
|
#include "VideoBackends/Software/SWCommandProcessor.h"
|
|
|
|
#include "VideoBackends/Software/SWRenderer.h"
|
|
|
|
#include "VideoBackends/Software/SWStatistics.h"
|
|
|
|
#include "VideoCommon/ImageWrite.h"
|
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
2013-04-13 05:48:53 +00:00
|
|
|
|
2010-06-09 01:37:08 +00:00
|
|
|
static GLuint s_RenderTarget = 0;
|
|
|
|
|
2012-12-17 20:54:20 +00:00
|
|
|
static GLint attr_pos = -1, attr_tex = -1;
|
|
|
|
static GLint uni_tex = -1;
|
|
|
|
static GLuint program;
|
|
|
|
|
2013-11-23 11:52:17 +00:00
|
|
|
static u8 *s_xfbColorTexture[2];
|
2013-08-20 11:51:39 +00:00
|
|
|
static int s_currentColorTexture = 0;
|
|
|
|
|
2013-11-16 04:07:08 +00:00
|
|
|
static volatile bool s_bScreenshot;
|
|
|
|
static std::mutex s_criticalScreenshot;
|
|
|
|
static std::string s_sScreenshotName;
|
|
|
|
|
|
|
|
|
2012-12-24 15:37:12 +00:00
|
|
|
// Rasterfont isn't compatible with GLES
|
2013-03-17 11:37:37 +00:00
|
|
|
// degasus: I think it does, but I can't test it
|
2014-07-08 13:58:25 +00:00
|
|
|
static RasterFont* s_pfont = nullptr;
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::Init()
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2013-11-16 04:07:08 +00:00
|
|
|
s_bScreenshot = false;
|
2010-06-09 01:37:08 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::Shutdown()
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2014-01-24 22:06:18 +00:00
|
|
|
delete [] s_xfbColorTexture[0];
|
|
|
|
delete [] s_xfbColorTexture[1];
|
2012-12-17 20:54:20 +00:00
|
|
|
glDeleteProgram(program);
|
2013-10-29 05:23:17 +00:00
|
|
|
glDeleteTextures(1, &s_RenderTarget);
|
2014-01-18 04:11:59 +00:00
|
|
|
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
|
|
|
|
{
|
|
|
|
delete s_pfont;
|
2014-03-09 20:14:26 +00:00
|
|
|
s_pfont = nullptr;
|
2014-01-18 04:11:59 +00:00
|
|
|
}
|
2012-12-17 20:54:20 +00:00
|
|
|
}
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2014-07-08 12:29:26 +00:00
|
|
|
static void CreateShaders()
|
2012-12-17 20:54:20 +00:00
|
|
|
{
|
|
|
|
static const char *fragShaderText =
|
2014-03-29 20:10:43 +00:00
|
|
|
"#ifdef GL_ES\n"
|
2014-01-18 04:11:59 +00:00
|
|
|
"precision highp float;\n"
|
|
|
|
"#endif\n"
|
|
|
|
"varying vec2 TexCoordOut;\n"
|
2013-01-19 06:51:00 +00:00
|
|
|
"uniform sampler2D Texture;\n"
|
2012-12-17 20:54:20 +00:00
|
|
|
"void main() {\n"
|
2014-01-18 04:11:59 +00:00
|
|
|
" gl_FragColor = texture2D(Texture, TexCoordOut);\n"
|
2012-12-17 20:54:20 +00:00
|
|
|
"}\n";
|
|
|
|
static const char *vertShaderText =
|
2014-03-29 20:10:43 +00:00
|
|
|
"#ifdef GL_ES\n"
|
2014-01-18 04:11:59 +00:00
|
|
|
"precision highp float;\n"
|
|
|
|
"#endif\n"
|
2012-12-17 20:54:20 +00:00
|
|
|
"attribute vec4 pos;\n"
|
|
|
|
"attribute vec2 TexCoordIn;\n "
|
|
|
|
"varying vec2 TexCoordOut;\n "
|
|
|
|
"void main() {\n"
|
2013-01-19 06:51:00 +00:00
|
|
|
" gl_Position = pos;\n"
|
2012-12-17 20:54:20 +00:00
|
|
|
" TexCoordOut = TexCoordIn;\n"
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
program = OpenGL_CompileProgram(vertShaderText, fragShaderText);
|
|
|
|
|
|
|
|
glUseProgram(program);
|
|
|
|
|
|
|
|
uni_tex = glGetUniformLocation(program, "Texture");
|
|
|
|
attr_pos = glGetAttribLocation(program, "pos");
|
2013-10-29 05:23:17 +00:00
|
|
|
attr_tex = glGetAttribLocation(program, "TexCoordIn");
|
2010-06-09 01:37:08 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::Prepare()
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2013-11-23 11:52:17 +00:00
|
|
|
s_xfbColorTexture[0] = new u8[640*568*4];
|
|
|
|
s_xfbColorTexture[1] = new u8[640*568*4];
|
|
|
|
|
2013-08-20 11:51:39 +00:00
|
|
|
s_currentColorTexture = 0;
|
|
|
|
|
2012-12-17 20:54:20 +00:00
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
2012-12-26 16:33:45 +00:00
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
|
|
|
glGenTextures(1, &s_RenderTarget);
|
2012-12-17 20:54:20 +00:00
|
|
|
|
|
|
|
CreateShaders();
|
2012-12-26 18:54:58 +00:00
|
|
|
// TODO: Enable for GLES once RasterFont supports GLES
|
2014-01-18 04:11:59 +00:00
|
|
|
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
|
|
|
|
{
|
|
|
|
s_pfont = new RasterFont();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
2012-12-17 20:54:20 +00:00
|
|
|
GL_REPORT_ERRORD();
|
2010-06-09 01:37:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-16 04:07:08 +00:00
|
|
|
void SWRenderer::SetScreenshot(const char *_szFilename)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
|
|
|
s_sScreenshotName = _szFilename;
|
|
|
|
s_bScreenshot = true;
|
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::RenderText(const char* pstr, int left, int top, u32 color)
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2014-01-18 04:11:59 +00:00
|
|
|
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
|
|
|
|
return;
|
2012-12-26 19:15:19 +00:00
|
|
|
int nBackbufferWidth = (int)GLInterface->GetBackBufferWidth();
|
|
|
|
int nBackbufferHeight = (int)GLInterface->GetBackBufferHeight();
|
2013-03-25 23:05:46 +00:00
|
|
|
glColor4f(((color>>16) & 0xff)/255.0f, ((color>> 8) & 0xff)/255.0f,
|
|
|
|
((color>> 0) & 0xff)/255.0f, ((color>>24) & 0xFF)/255.0f);
|
2010-06-09 01:37:08 +00:00
|
|
|
s_pfont->printMultilineText(pstr,
|
2013-03-25 23:05:46 +00:00
|
|
|
left * 2.0f / (float)nBackbufferWidth - 1,
|
|
|
|
1 - top * 2.0f / (float)nBackbufferHeight,
|
|
|
|
0, nBackbufferWidth, nBackbufferHeight);
|
2010-06-09 01:37:08 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::DrawDebugText()
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2014-06-03 05:08:54 +00:00
|
|
|
std::string debugtext;
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2013-10-29 05:23:17 +00:00
|
|
|
if (g_SWVideoConfig.bShowStats)
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2014-06-03 05:08:54 +00:00
|
|
|
debugtext += StringFromFormat("Objects: %i\n", swstats.thisFrame.numDrawnObjects);
|
|
|
|
debugtext += StringFromFormat("Primitives: %i\n", swstats.thisFrame.numPrimatives);
|
|
|
|
debugtext += StringFromFormat("Vertices Loaded: %i\n", swstats.thisFrame.numVerticesLoaded);
|
|
|
|
|
|
|
|
debugtext += StringFromFormat("Triangles Input: %i\n", swstats.thisFrame.numTrianglesIn);
|
|
|
|
debugtext += StringFromFormat("Triangles Rejected: %i\n", swstats.thisFrame.numTrianglesRejected);
|
|
|
|
debugtext += StringFromFormat("Triangles Culled: %i\n", swstats.thisFrame.numTrianglesCulled);
|
|
|
|
debugtext += StringFromFormat("Triangles Clipped: %i\n", swstats.thisFrame.numTrianglesClipped);
|
|
|
|
debugtext += StringFromFormat("Triangles Drawn: %i\n", swstats.thisFrame.numTrianglesDrawn);
|
|
|
|
|
|
|
|
debugtext += StringFromFormat("Rasterized Pix: %i\n", swstats.thisFrame.rasterizedPixels);
|
|
|
|
debugtext += StringFromFormat("TEV Pix In: %i\n", swstats.thisFrame.tevPixelsIn);
|
|
|
|
debugtext += StringFromFormat("TEV Pix Out: %i\n", swstats.thisFrame.tevPixelsOut);
|
2013-04-14 03:54:02 +00:00
|
|
|
}
|
2010-06-09 01:37:08 +00:00
|
|
|
|
|
|
|
// Render a shadow, and then the text.
|
2014-06-03 05:08:54 +00:00
|
|
|
SWRenderer::RenderText(debugtext.c_str(), 21, 21, 0xDD000000);
|
|
|
|
SWRenderer::RenderText(debugtext.c_str(), 20, 20, 0xFFFFFF00);
|
2010-06-09 01:37:08 +00:00
|
|
|
}
|
2013-09-02 06:40:05 +00:00
|
|
|
|
2014-07-14 11:51:55 +00:00
|
|
|
u8* SWRenderer::getNextColorTexture() {
|
2013-11-23 07:04:37 +00:00
|
|
|
return s_xfbColorTexture[!s_currentColorTexture];
|
|
|
|
}
|
|
|
|
|
2014-07-14 11:51:55 +00:00
|
|
|
u8* SWRenderer::getCurrentColorTexture() {
|
|
|
|
return s_xfbColorTexture[s_currentColorTexture];
|
|
|
|
}
|
|
|
|
|
2013-11-23 07:04:37 +00:00
|
|
|
void SWRenderer::swapColorTexture() {
|
|
|
|
s_currentColorTexture = !s_currentColorTexture;
|
|
|
|
}
|
|
|
|
|
2013-11-23 05:38:15 +00:00
|
|
|
void SWRenderer::UpdateColorTexture(EfbInterface::yuv422_packed *xfb, u32 fbWidth, u32 fbHeight)
|
2013-08-20 11:51:39 +00:00
|
|
|
{
|
2014-03-10 11:30:55 +00:00
|
|
|
if (fbWidth*fbHeight > 640*568) {
|
2013-11-23 11:15:08 +00:00
|
|
|
ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-20 11:51:39 +00:00
|
|
|
u32 offset = 0;
|
2014-07-14 11:51:55 +00:00
|
|
|
u8 *TexturePointer = getNextColorTexture();
|
2013-08-20 11:51:39 +00:00
|
|
|
|
2013-11-23 05:38:15 +00:00
|
|
|
for (u16 y = 0; y < fbHeight; y++)
|
2013-08-20 11:51:39 +00:00
|
|
|
{
|
2013-11-23 05:38:15 +00:00
|
|
|
for (u16 x = 0; x < fbWidth; x+=2)
|
2013-08-20 11:51:39 +00:00
|
|
|
{
|
|
|
|
// We do this one color sample (aka 2 RGB pixles) at a time
|
|
|
|
int Y1 = xfb[x].Y - 16;
|
|
|
|
int Y2 = xfb[x+1].Y - 16;
|
|
|
|
int U = int(xfb[x].UV) - 128;
|
|
|
|
int V = int(xfb[x+1].UV) - 128;
|
|
|
|
|
|
|
|
// We do the inverse BT.601 conversion for YCbCr to RGB
|
|
|
|
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
|
2014-07-14 12:42:33 +00:00
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y1 + 1.596f * V), 0, 255);
|
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y1 - 0.392f * U - 0.813f * V), 0, 255);
|
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y1 + 2.017f * U ), 0, 255);
|
2013-08-20 11:51:39 +00:00
|
|
|
TexturePointer[offset++] = 255;
|
|
|
|
|
2014-07-14 12:42:33 +00:00
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y2 + 1.596f * V), 0, 255);
|
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y2 - 0.392f * U - 0.813f * V), 0, 255);
|
|
|
|
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y2 + 2.017f * U ), 0, 255);
|
2013-08-20 11:51:39 +00:00
|
|
|
TexturePointer[offset++] = 255;
|
|
|
|
}
|
2013-11-23 05:38:15 +00:00
|
|
|
xfb += fbWidth;
|
2013-08-20 11:51:39 +00:00
|
|
|
}
|
2013-11-23 07:04:37 +00:00
|
|
|
swapColorTexture();
|
2013-08-20 11:51:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called on the GPU thread
|
|
|
|
void SWRenderer::Swap(u32 fbWidth, u32 fbHeight)
|
|
|
|
{
|
|
|
|
GLInterface->Update(); // just updates the render window position and the backbuffer size
|
|
|
|
if (!g_SWVideoConfig.bHwRasterizer)
|
2014-07-14 11:51:55 +00:00
|
|
|
SWRenderer::DrawTexture(getCurrentColorTexture(), fbWidth, fbHeight);
|
2013-08-20 11:51:39 +00:00
|
|
|
|
|
|
|
swstats.frameCount++;
|
|
|
|
SWRenderer::SwapBuffer();
|
|
|
|
Core::Callback_VideoCopiedToXFB(true); // FIXME: should this function be called FrameRendered?
|
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::DrawTexture(u8 *texture, int width, int height)
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2013-08-20 11:51:39 +00:00
|
|
|
// FIXME: This should add black bars when the game has set the VI to render less than the full xfb.
|
|
|
|
|
2013-11-16 04:07:08 +00:00
|
|
|
// Save screenshot
|
|
|
|
if (s_bScreenshot)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
2013-11-16 22:12:07 +00:00
|
|
|
TextureToPng(texture, width*4, s_sScreenshotName, width, height, false);
|
2013-11-16 04:07:08 +00:00
|
|
|
// Reset settings
|
|
|
|
s_sScreenshotName.clear();
|
|
|
|
s_bScreenshot = false;
|
|
|
|
}
|
2013-11-23 10:20:45 +00:00
|
|
|
|
2012-12-26 16:33:45 +00:00
|
|
|
GLsizei glWidth = (GLsizei)GLInterface->GetBackBufferWidth();
|
2012-12-17 20:54:20 +00:00
|
|
|
GLsizei glHeight = (GLsizei)GLInterface->GetBackBufferHeight();
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2013-08-20 11:51:39 +00:00
|
|
|
|
2010-06-09 01:37:08 +00:00
|
|
|
// Update GLViewPort
|
|
|
|
glViewport(0, 0, glWidth, glHeight);
|
2012-12-26 16:33:45 +00:00
|
|
|
glScissor(0, 0, glWidth, glHeight);
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2013-01-19 06:51:00 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, s_RenderTarget);
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2013-01-19 06:51:00 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2013-01-19 08:18:39 +00:00
|
|
|
glUseProgram(program);
|
2012-12-17 20:54:20 +00:00
|
|
|
static const GLfloat verts[4][2] = {
|
|
|
|
{ -1, -1}, // Left top
|
|
|
|
{ -1, 1}, // left bottom
|
|
|
|
{ 1, 1}, // right bottom
|
|
|
|
{ 1, -1} // right top
|
|
|
|
};
|
|
|
|
static const GLfloat texverts[4][2] = {
|
|
|
|
{0, 1},
|
|
|
|
{0, 0},
|
|
|
|
{1, 0},
|
|
|
|
{1, 1}
|
|
|
|
};
|
2013-01-19 08:18:39 +00:00
|
|
|
|
2012-12-17 20:54:20 +00:00
|
|
|
glVertexAttribPointer(attr_pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
|
|
|
glVertexAttribPointer(attr_tex, 2, GL_FLOAT, GL_FALSE, 0, texverts);
|
2012-12-26 16:33:45 +00:00
|
|
|
glEnableVertexAttribArray(attr_pos);
|
2012-12-17 20:54:20 +00:00
|
|
|
glEnableVertexAttribArray(attr_tex);
|
2013-04-14 03:54:02 +00:00
|
|
|
glUniform1i(uni_tex, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
2012-12-26 16:33:45 +00:00
|
|
|
glDisableVertexAttribArray(attr_pos);
|
2012-12-17 20:54:20 +00:00
|
|
|
glDisableVertexAttribArray(attr_tex);
|
|
|
|
|
2013-10-29 05:23:17 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2010-06-09 01:37:08 +00:00
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
}
|
|
|
|
|
2011-02-03 19:55:30 +00:00
|
|
|
void SWRenderer::SwapBuffer()
|
2010-06-09 01:37:08 +00:00
|
|
|
{
|
2013-10-29 05:23:17 +00:00
|
|
|
// Do our OSD callbacks
|
2013-04-13 05:48:53 +00:00
|
|
|
OSD::DoCallbacks(OSD::OSD_ONFRAME);
|
|
|
|
|
2012-12-26 16:33:45 +00:00
|
|
|
DrawDebugText();
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2012-12-26 16:33:45 +00:00
|
|
|
glFlush();
|
2010-06-09 01:37:08 +00:00
|
|
|
|
2012-12-26 06:34:09 +00:00
|
|
|
GLInterface->Swap();
|
2013-04-14 03:54:02 +00:00
|
|
|
|
2013-01-19 06:51:00 +00:00
|
|
|
swstats.ResetFrame();
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2010-06-09 01:37:08 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
}
|