summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Software/SWRenderer.cpp
diff options
context:
space:
mode:
authorJules Blok <jules.blok@gmail.com>2015-06-10 00:00:12 +0200
committerJules Blok <jules.blok@gmail.com>2015-06-10 00:00:12 +0200
commit7dfced21a2e5aac7195e0e1ad76468e30306766b (patch)
treebd671b639d3d911460b767caabad8fc8a759a6bf /Source/Core/VideoBackends/Software/SWRenderer.cpp
parentc25be031fc1031d795157d8344b87b7841145197 (diff)
parent7b0a65e295c784f9d573f2ef52d4e2a7b9bb2889 (diff)
Merge branch 'master' into stable
Diffstat (limited to 'Source/Core/VideoBackends/Software/SWRenderer.cpp')
-rw-r--r--Source/Core/VideoBackends/Software/SWRenderer.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp
new file mode 100644
index 0000000000..761cc667df
--- /dev/null
+++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp
@@ -0,0 +1,291 @@
+// Copyright 2009 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <atomic>
+#include <mutex>
+#include <string>
+
+#include "Common/CommonTypes.h"
+#include "Common/StringUtil.h"
+#include "Core/Core.h"
+#include "VideoBackends/OGL/GLInterfaceBase.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"
+
+static GLuint s_RenderTarget = 0;
+
+static GLint attr_pos = -1, attr_tex = -1;
+static GLint uni_tex = -1;
+static GLuint program;
+
+static u8 *s_xfbColorTexture[2];
+static int s_currentColorTexture = 0;
+
+static std::atomic<bool> s_bScreenshot;
+static std::mutex s_criticalScreenshot;
+static std::string s_sScreenshotName;
+
+
+// Rasterfont isn't compatible with GLES
+// degasus: I think it does, but I can't test it
+static RasterFont* s_pfont = nullptr;
+
+void SWRenderer::Init()
+{
+ s_bScreenshot.store(false);
+}
+
+void SWRenderer::Shutdown()
+{
+ delete[] s_xfbColorTexture[0];
+ delete[] s_xfbColorTexture[1];
+ glDeleteProgram(program);
+ glDeleteTextures(1, &s_RenderTarget);
+ if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
+ {
+ delete s_pfont;
+ s_pfont = nullptr;
+ }
+}
+
+static void CreateShaders()
+{
+ static const char *fragShaderText =
+ "#ifdef GL_ES\n"
+ "precision highp float;\n"
+ "#endif\n"
+ "varying vec2 TexCoordOut;\n"
+ "uniform sampler2D Texture;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(Texture, TexCoordOut);\n"
+ "}\n";
+ static const char *vertShaderText =
+ "#ifdef GL_ES\n"
+ "precision highp float;\n"
+ "#endif\n"
+ "attribute vec4 pos;\n"
+ "attribute vec2 TexCoordIn;\n "
+ "varying vec2 TexCoordOut;\n "
+ "void main() {\n"
+ " gl_Position = pos;\n"
+ " TexCoordOut = TexCoordIn;\n"
+ "}\n";
+
+ program = OpenGL_CompileProgram(vertShaderText, fragShaderText);
+
+ glUseProgram(program);
+
+ uni_tex = glGetUniformLocation(program, "Texture");
+ attr_pos = glGetAttribLocation(program, "pos");
+ attr_tex = glGetAttribLocation(program, "TexCoordIn");
+}
+
+void SWRenderer::Prepare()
+{
+ s_xfbColorTexture[0] = new u8[MAX_XFB_WIDTH * MAX_XFB_HEIGHT * 4];
+ s_xfbColorTexture[1] = new u8[MAX_XFB_WIDTH * MAX_XFB_HEIGHT * 4];
+
+ s_currentColorTexture = 0;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
+ glGenTextures(1, &s_RenderTarget);
+
+ CreateShaders();
+ // TODO: Enable for GLES once RasterFont supports GLES
+ if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
+ {
+ s_pfont = new RasterFont();
+ glEnable(GL_TEXTURE_2D);
+ }
+}
+
+void SWRenderer::SetScreenshot(const char *_szFilename)
+{
+ std::lock_guard<std::mutex> lk(s_criticalScreenshot);
+ s_sScreenshotName = _szFilename;
+ s_bScreenshot.store(true);
+}
+
+void SWRenderer::RenderText(const char* pstr, int left, int top, u32 color)
+{
+ if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
+ return;
+ int nBackbufferWidth = (int)GLInterface->GetBackBufferWidth();
+ int nBackbufferHeight = (int)GLInterface->GetBackBufferHeight();
+ glColor4f(((color >> 16) & 0xff) / 255.0f, ((color >> 8) & 0xff) / 255.0f,
+ ((color >> 0) & 0xff) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
+ s_pfont->printMultilineText(pstr,
+ left * 2.0f / (float)nBackbufferWidth - 1,
+ 1 - top * 2.0f / (float)nBackbufferHeight,
+ 0, nBackbufferWidth, nBackbufferHeight);
+}
+
+void SWRenderer::DrawDebugText()
+{
+ std::string debugtext;
+
+ if (g_SWVideoConfig.bShowStats)
+ {
+ 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);
+ }
+
+ // Render a shadow, and then the text.
+ SWRenderer::RenderText(debugtext.c_str(), 21, 21, 0xDD000000);
+ SWRenderer::RenderText(debugtext.c_str(), 20, 20, 0xFFFFFF00);
+}
+
+u8* SWRenderer::GetNextColorTexture()
+{
+ return s_xfbColorTexture[!s_currentColorTexture];
+}
+
+u8* SWRenderer::GetCurrentColorTexture()
+{
+ return s_xfbColorTexture[s_currentColorTexture];
+}
+
+void SWRenderer::SwapColorTexture()
+{
+ s_currentColorTexture = !s_currentColorTexture;
+}
+
+void SWRenderer::UpdateColorTexture(EfbInterface::yuv422_packed *xfb, u32 fbWidth, u32 fbHeight)
+{
+ if (fbWidth * fbHeight > MAX_XFB_WIDTH * MAX_XFB_HEIGHT)
+ {
+ ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
+ return;
+ }
+
+ u32 offset = 0;
+ u8 *TexturePointer = GetNextColorTexture();
+
+ for (u16 y = 0; y < fbHeight; y++)
+ {
+ for (u16 x = 0; x < fbWidth; x+=2)
+ {
+ // 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
+ 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);
+ TexturePointer[offset++] = 255;
+
+ 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);
+ TexturePointer[offset++] = 255;
+ }
+ xfb += fbWidth;
+ }
+ SwapColorTexture();
+}
+
+// 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)
+ SWRenderer::DrawTexture(GetCurrentColorTexture(), fbWidth, fbHeight);
+
+ swstats.frameCount++;
+ SWRenderer::SwapBuffer();
+ Core::Callback_VideoCopiedToXFB(true); // FIXME: should this function be called FrameRendered?
+}
+
+void SWRenderer::DrawTexture(u8 *texture, int width, int height)
+{
+ // FIXME: This should add black bars when the game has set the VI to render less than the full xfb.
+
+ // Save screenshot
+ if (s_bScreenshot.load())
+ {
+ std::lock_guard<std::mutex> lk(s_criticalScreenshot);
+ TextureToPng(texture, width * 4, s_sScreenshotName, width, height, false);
+ // Reset settings
+ s_sScreenshotName.clear();
+ s_bScreenshot.store(false);
+ }
+
+ GLsizei glWidth = (GLsizei)GLInterface->GetBackBufferWidth();
+ GLsizei glHeight = (GLsizei)GLInterface->GetBackBufferHeight();
+
+
+ // Update GLViewPort
+ glViewport(0, 0, glWidth, glHeight);
+ glScissor(0, 0, glWidth, glHeight);
+
+ glBindTexture(GL_TEXTURE_2D, s_RenderTarget);
+
+ 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);
+
+ glUseProgram(program);
+ 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}
+ };
+
+ glVertexAttribPointer(attr_pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(attr_tex, 2, GL_FLOAT, GL_FALSE, 0, texverts);
+ glEnableVertexAttribArray(attr_pos);
+ glEnableVertexAttribArray(attr_tex);
+ glUniform1i(uni_tex, 0);
+ glActiveTexture(GL_TEXTURE0);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glDisableVertexAttribArray(attr_pos);
+ glDisableVertexAttribArray(attr_tex);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void SWRenderer::SwapBuffer()
+{
+ // Do our OSD callbacks
+ OSD::DoCallbacks(OSD::OSD_ONFRAME);
+
+ DrawDebugText();
+
+ glFlush();
+
+ GLInterface->Swap();
+
+ swstats.ResetFrame();
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}