From fa3e7a0584840dee13d7e0e843f426519d94ff5d Mon Sep 17 00:00:00 2001 From: Sergey Lipskiy Date: Wed, 11 Jan 2017 17:07:20 +0700 Subject: [PATCH] GraphicsDrawer WIP --- projects/msvc12/GLideN64.vcxproj | 4 +- projects/msvc12/GLideN64.vcxproj.filters | 12 +- src/Graphics/Context.cpp | 20 + src/Graphics/Context.h | 11 +- src/Graphics/ContextImpl.h | 4 + .../{DrawerImpl.h => GraphicsDrawerImpl.h} | 4 +- .../OpenGLContext/opengl_CachedFunctions.cpp | 15 + .../OpenGLContext/opengl_CachedFunctions.h | 9 + .../OpenGLContext/opengl_ContextImpl.cpp | 44 +- .../OpenGLContext/opengl_ContextImpl.h | 8 + .../OpenGLContext/opengl_DummyTextDrawer.h | 20 + .../OpenGLContext/opengl_TextDrawerImpl.cpp | 408 ++++++++++ .../OpenGLContext/opengl_UnbufferedDrawer.cpp | 40 +- .../OpenGLContext/opengl_UnbufferedDrawer.h | 8 +- src/Graphics/TextDrawerImpl.h | 16 + src/GraphicsDrawer.cpp | 761 +++++++++++++++++- src/GraphicsDrawer.h | 18 +- 17 files changed, 1371 insertions(+), 31 deletions(-) rename src/Graphics/{DrawerImpl.h => GraphicsDrawerImpl.h} (88%) create mode 100644 src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h create mode 100644 src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp create mode 100644 src/Graphics/TextDrawerImpl.h diff --git a/projects/msvc12/GLideN64.vcxproj b/projects/msvc12/GLideN64.vcxproj index 7304526c..2932b6ce 100644 --- a/projects/msvc12/GLideN64.vcxproj +++ b/projects/msvc12/GLideN64.vcxproj @@ -438,7 +438,7 @@ - + @@ -454,6 +454,7 @@ + @@ -461,6 +462,7 @@ + diff --git a/projects/msvc12/GLideN64.vcxproj.filters b/projects/msvc12/GLideN64.vcxproj.filters index 4cf4cf82..79c9629d 100644 --- a/projects/msvc12/GLideN64.vcxproj.filters +++ b/projects/msvc12/GLideN64.vcxproj.filters @@ -628,14 +628,20 @@ Header Files - - Header Files\Graphics - Header Files\Graphics\OpenGL Header Files + + Header Files\Graphics + + + Header Files\Graphics + + + Header Files\Graphics\OpenGL + \ No newline at end of file diff --git a/src/Graphics/Context.cpp b/src/Graphics/Context.cpp index 9557bca3..0f289e0d 100644 --- a/src/Graphics/Context.cpp +++ b/src/Graphics/Context.cpp @@ -63,6 +63,16 @@ void Context::setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) m_impl->setBlendColor(_red, _green, _blue, _alpha); } +void Context::clearColorBuffer(f32 _red, f32 _green, f32 _blue, f32 _alpha) +{ + m_impl->clearColorBuffer(_red, _green, _blue, _alpha); +} + +void Context::clearDepthBuffer() +{ + m_impl->clearDepthBuffer(); +} + ObjectHandle Context::createTexture(Parameter _target) { return m_impl->createTexture(_target); @@ -163,6 +173,16 @@ DrawerImpl * Context::createDrawerImpl() return m_impl->createDrawerImpl(); } +TextDrawer * Context::createTextDrawer() +{ + return m_impl->createTextDrawer(); +} + +f32 Context::getMaxLineWidth() +{ + return m_impl->getMaxLineWidth(); +} + bool Context::isSupported(SpecialFeatures _feature) const { // TODO diff --git a/src/Graphics/Context.h b/src/Graphics/Context.h index e944aad4..5ada63e9 100644 --- a/src/Graphics/Context.h +++ b/src/Graphics/Context.h @@ -7,7 +7,8 @@ #include "CombinerProgram.h" #include "ShaderProgram.h" #include "PixelBuffer.h" -#include "DrawerImpl.h" +#include "GraphicsDrawerImpl.h" +#include "TextDrawerImpl.h" #define GRAPHICS_CONTEXT @@ -47,6 +48,10 @@ namespace graphics { void setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha); + void clearColorBuffer(f32 _red, f32 _green, f32 _blue, f32 _alpha); + + void clearDepthBuffer(); + ObjectHandle createTexture(Parameter _target); void deleteTexture(ObjectHandle _name); @@ -144,6 +149,10 @@ namespace graphics { DrawerImpl * createDrawerImpl(); + TextDrawer * createTextDrawer(); + + f32 getMaxLineWidth(); + bool isSupported(SpecialFeatures _feature) const; private: diff --git a/src/Graphics/ContextImpl.h b/src/Graphics/ContextImpl.h index 9b414585..5da75f7c 100644 --- a/src/Graphics/ContextImpl.h +++ b/src/Graphics/ContextImpl.h @@ -20,6 +20,8 @@ namespace graphics { virtual void setScissor(s32 _x, s32 _y, s32 _width, s32 _height) = 0; virtual void setBlending(Parameter _sfactor, Parameter _dfactor) = 0; virtual void setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) = 0; + virtual void clearColorBuffer(f32 _red, f32 _green, f32 _blue, f32 _alpha) = 0; + virtual void clearDepthBuffer() = 0; virtual ObjectHandle createTexture(Parameter _target) = 0; virtual void deleteTexture(ObjectHandle _name) = 0; virtual void init2DTexture(const Context::InitTextureParams & _params) = 0; @@ -40,6 +42,8 @@ namespace graphics { virtual ShaderProgram * createTexDrawerClearShader() = 0; virtual ShaderProgram * createTexrectCopyShader() = 0; virtual DrawerImpl * createDrawerImpl() = 0; + virtual TextDrawer * createTextDrawer() = 0; + virtual f32 getMaxLineWidth() = 0; }; } diff --git a/src/Graphics/DrawerImpl.h b/src/Graphics/GraphicsDrawerImpl.h similarity index 88% rename from src/Graphics/DrawerImpl.h rename to src/Graphics/GraphicsDrawerImpl.h index 45806ef6..078c02e3 100644 --- a/src/Graphics/DrawerImpl.h +++ b/src/Graphics/GraphicsDrawerImpl.h @@ -1,5 +1,6 @@ #ifndef GRAPHICS_DRAWERIMPL_H #define GRAPHICS_DRAWERIMPL_H +#include #include #include "Parameter.h" #include "CombinerProgram.h" @@ -27,12 +28,13 @@ namespace graphics { { Parameter mode; u32 verticesCount = 0; - f32 rectColor[4]; + std::array rectColor; RectVertex * vertices = nullptr; const CombinerProgram * combiner = nullptr; }; virtual void drawRects(const DrawRectParameters & _params) = 0; + virtual void drawLine(f32 _width, SPVertex * vertices) = 0; }; } diff --git a/src/Graphics/OpenGLContext/opengl_CachedFunctions.cpp b/src/Graphics/OpenGLContext/opengl_CachedFunctions.cpp index 2bc2f20a..cb61c6bf 100644 --- a/src/Graphics/OpenGLContext/opengl_CachedFunctions.cpp +++ b/src/Graphics/OpenGLContext/opengl_CachedFunctions.cpp @@ -98,6 +98,14 @@ void CachedBlendColor::setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha glBlendColor(_red, _green, _blue, _alpha); } +/*---------------CachedClearColor-------------*/ + +void CachedClearColor::setClearColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) +{ + if (update(Parameter(_red), Parameter(_green), Parameter(_blue), Parameter(_alpha))) + glClearColor(_red, _green, _blue, _alpha); +} + /*---------------CachedVertexAttribArray-------------*/ void CachedVertexAttribArray::enableVertexAttribArray(u32 _index, bool _enable) @@ -150,6 +158,7 @@ void CachedFunctions::reset() m_scissor.reset(); m_blending.reset(); m_blendColor.reset(); + m_clearColor.reset(); m_attribArray.reset(); } @@ -226,6 +235,12 @@ CachedBlendColor * CachedFunctions::getCachedBlendColor() return &m_blendColor; } +CachedClearColor * CachedFunctions::getCachedClearColor() +{ + return &m_clearColor; +} + + CachedVertexAttribArray * CachedFunctions::getCachedVertexAttribArray() { return &m_attribArray; diff --git a/src/Graphics/OpenGLContext/opengl_CachedFunctions.h b/src/Graphics/OpenGLContext/opengl_CachedFunctions.h index 75044a59..2c11768c 100644 --- a/src/Graphics/OpenGLContext/opengl_CachedFunctions.h +++ b/src/Graphics/OpenGLContext/opengl_CachedFunctions.h @@ -180,6 +180,12 @@ namespace opengl { void setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha); }; + class CachedClearColor : public Cached4 + { + public: + void setClearColor(f32 _red, f32 _green, f32 _blue, f32 _alpha); + }; + class CachedVertexAttribArray { public: void enableVertexAttribArray(u32 _index, bool _enable); @@ -225,6 +231,8 @@ namespace opengl { CachedBlendColor * getCachedBlendColor(); + CachedClearColor * getCachedClearColor(); + CachedVertexAttribArray * getCachedVertexAttribArray(); private: @@ -243,6 +251,7 @@ namespace opengl { CachedScissor m_scissor; CachedBlending m_blending; CachedBlendColor m_blendColor; + CachedClearColor m_clearColor; CachedVertexAttribArray m_attribArray; }; diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp index fa11db66..da5dffef 100644 --- a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp +++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp @@ -1,7 +1,9 @@ #include #include +#include #include "opengl_ContextImpl.h" #include "opengl_UnbufferedDrawer.h" +#include "opengl_DummyTextDrawer.h" #include "GLSL/glsl_CombinerProgramBuilder.h" #include "GLSL/glsl_SpecialShadersFactory.h" #include "GLSL/glsl_ShaderStorage.h" @@ -98,7 +100,35 @@ void ContextImpl::setBlending(graphics::Parameter _sfactor, graphics::Parameter void ContextImpl::setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) { + m_cachedFunctions->getCachedBlendColor()->setBlendColor(_red, _green, _blue, _alpha); +} +void ContextImpl::clearColorBuffer(f32 _red, f32 _green, f32 _blue, f32 _alpha) +{ + CachedEnable * enableScissor = m_cachedFunctions->getCachedEnable(graphics::enable::SCISSOR_TEST); + enableScissor->enable(false); + + m_cachedFunctions->getCachedClearColor()->setClearColor(_red, _green, _blue, _alpha); + glClear(GL_COLOR_BUFFER_BIT); + + enableScissor->enable(true); +} + +void ContextImpl::clearDepthBuffer() +{ + CachedEnable * enableScissor = m_cachedFunctions->getCachedEnable(graphics::enable::SCISSOR_TEST); + CachedDepthMask * depthMask = m_cachedFunctions->getCachedDepthMask(); + enableScissor->enable(false); + +#ifdef ANDROID + depthMask->setDepthMask(false); + glClear(GL_DEPTH_BUFFER_BIT); +#endif + + depthMask->setDepthMask(true); + glClear(GL_DEPTH_BUFFER_BIT); + + enableScissor->enable(true); } graphics::ObjectHandle ContextImpl::createTexture(graphics::Parameter _target) @@ -224,5 +254,17 @@ graphics::ShaderProgram * ContextImpl::createTexrectCopyShader() graphics::DrawerImpl * ContextImpl::createDrawerImpl() { - return new UnbufferedDrawer(m_cachedFunctions->getCachedVertexAttribArray()); + return new UnbufferedDrawer(m_glInfo, m_cachedFunctions->getCachedVertexAttribArray()); +} + +graphics::TextDrawer * ContextImpl::createTextDrawer() +{ + return new DummyTextDrawer; +} + +f32 ContextImpl::getMaxLineWidth() +{ + GLfloat lineWidthRange[2] = { 0.0f, 0.0f }; + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange); + return lineWidthRange[1]; } diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.h b/src/Graphics/OpenGLContext/opengl_ContextImpl.h index ce46a03d..6ffb6444 100644 --- a/src/Graphics/OpenGLContext/opengl_ContextImpl.h +++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.h @@ -38,6 +38,10 @@ namespace opengl { void setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) override; + void clearColorBuffer(f32 _red, f32 _green, f32 _blue, f32 _alpha) override; + + void clearDepthBuffer() override; + graphics::ObjectHandle createTexture(graphics::Parameter _target) override; void deleteTexture(graphics::ObjectHandle _name) override; @@ -78,6 +82,10 @@ namespace opengl { graphics::DrawerImpl * createDrawerImpl() override; + graphics::TextDrawer * createTextDrawer() override; + + f32 getMaxLineWidth() override; + private: std::unique_ptr m_cachedFunctions; std::unique_ptr m_createTexture; diff --git a/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h b/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h new file mode 100644 index 00000000..e9374768 --- /dev/null +++ b/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h @@ -0,0 +1,20 @@ +#ifndef OPENGL_DUMMY_TEXTDRAWER_H +#define OPENGL_DUMMY_TEXTDRAWER_H + +#include + +namespace opengl { + + class DummyTextDrawer : public graphics::TextDrawer + { + public: + DummyTextDrawer() {} + ~DummyTextDrawer() {} + + void renderText(const char *_pText, float x, float y) const override {} + void getTextSize(const char *_pText, float & _w, float & _h) const override {} + }; + +} + +#endif // OPENGL_DUMMY_TEXTDRAWER_H diff --git a/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp b/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp new file mode 100644 index 00000000..9319309a --- /dev/null +++ b/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp @@ -0,0 +1,408 @@ +/* Draw text on screen. + * Requires freetype library. + * Code is taken from "OpenGL source examples from the OpenGL Programming wikibook: + * http://en.wikibooks.org/wiki/OpenGL_Programming" + */ + +#define NOMINMAX + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "opengl_TextDrawerImpl.h" +#include +#include +#include +#include + +#include +#include + +//#include "ShaderUtils.h" + +struct point { + GLfloat x; + GLfloat y; + GLfloat s; + GLfloat t; + point() : x(0), y(0), s(0), t(0) {} + point(GLfloat _x, GLfloat _y, GLfloat _s, GLfloat _t) : x(_x), y(_y), s(_s), t(_t) {} +}; + +// Maximum texture width +#define MAXWIDTH 1024 + +#if defined(GLES3_1) +#define SHADER_VERSION "#version 310 es \n" +#elif defined(GLES3) +#define SHADER_VERSION "#version 300 es \n" +#elif defined(GLES2) +#define SHADER_VERSION "#version 100 \n" +#else +#define SHADER_VERSION "#version 330 core \n" +#endif + +#ifdef GLES2 +const GLenum monohromeformat = GL_LUMINANCE; +const GLenum monohromeInternalformat = GL_LUMINANCE; +#else +const GLenum monohromeformat = GL_RED; +const GLenum monohromeInternalformat = GL_R8; +#endif // GLES2 + +static +const char * strDrawTextVertexShader = +SHADER_VERSION +"#if (__VERSION__ > 120) \n" +"# define IN in \n" +"# define OUT out \n" +"#else \n" +"# define IN attribute \n" +"# define OUT varying \n" +"#endif // __VERSION \n" +"IN highp vec4 aRectPosition; \n" +"OUT mediump vec2 texpos; \n" +"void main(void) { \n" +" gl_Position = vec4(aRectPosition.xy, 0, 1); \n" +" texpos = aRectPosition.zw; \n" +"} \n" +; + +static +const char * strDrawTextFragmentShader = +SHADER_VERSION +"#if (__VERSION__ > 120) \n" +"# define IN in \n" +"# define OUT out \n" +"# define texture2D texture \n" +"#else \n" +"# define IN varying \n" +"# define OUT \n" +"#endif // __VERSION __ \n" +"IN mediump vec2 texpos; \n" +"uniform sampler2D uTex; \n" +"uniform lowp vec4 uColor; \n" +"OUT lowp vec4 fragColor; \n" +"void main(void) { \n" +" fragColor = texture2D(uTex, texpos).r * uColor; \n" +#ifdef GLES2 +" gl_FragColor = fragColor; \n" +#endif +"} \n" +; + +/** + * The atlas struct holds a texture that contains the visible US-ASCII characters + * of a certain font rendered with a certain character height. + * It also contains an array that contains all the information necessary to + * generate the appropriate vertex and texture coordinates for each character. + * + * After the constructor is run, you don't need to use any FreeType functions anymore. + */ +struct Atlas { + GLuint tex; // texture object + + int w; // width of texture in pixels + int h; // height of texture in pixels + + struct { + float ax; // advance.x + float ay; // advance.y + + float bw; // bitmap.width; + float bh; // bitmap.height; + + float bl; // bitmap_left; + float bt; // bitmap_top; + + float tx; // x offset of glyph in texture coordinates + float ty; // y offset of glyph in texture coordinates + } c[128]; // character information + + Atlas(FT_Face face, int height) { + FT_Set_Pixel_Sizes(face, 0, height); + FT_GlyphSlot g = face->glyph; + + int roww = 0; + int rowh = 0; + w = 0; + h = 0; + + memset(c, 0, sizeof c); + + /* Find minimum size for a texture holding all visible ASCII characters */ + for (int i = 32; i < 128; i++) { + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + fprintf(stderr, "Loading character %c failed!\n", i); + continue; + } + if (roww + g->bitmap.width + 1 >= MAXWIDTH) { + w = std::max(w, roww); + h += rowh; + roww = 0; + rowh = 0; + } + roww += g->bitmap.width + 1; + rowh = std::max(rowh, (int)g->bitmap.rows); + } + + w = std::max(w, roww); + h += rowh; + + /* Create a texture that will be used to hold all ASCII glyphs */ + + graphics::ObjectHandle texHandle = gfxContext.createTexture(graphics::target::TEXTURE_2D); + tex = GLuint(texHandle); + + graphics::Context::InitTextureParams initParams; + initParams.handle = texHandle; + initParams.width = w; + initParams.height = h; + initParams.internalFormat = monohromeInternalformat; + initParams.format = monohromeformat; + initParams.dataType = graphics::datatype::UNSIGNED_BYTE; + gfxContext.init2DTexture(initParams); + + /* We require 1 byte alignment when uploading texture data */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + graphics::Context::TexParameters setParams; + setParams.handle = texHandle; + setParams.target = graphics::target::TEXTURE_2D; + setParams.minFilter = graphics::textureParameters::FILTER_LINEAR; + setParams.magFilter = graphics::textureParameters::FILTER_LINEAR; + setParams.wrapS = graphics::textureParameters::WRAP_CLAMP_TO_EDGE; + setParams.wrapT = graphics::textureParameters::WRAP_CLAMP_TO_EDGE; + gfxContext.setTextureParameters(setParams); + + /* Paste all glyph bitmaps into the texture, remembering the offset */ + int ox = 0; + int oy = 0; + + rowh = 0; + + for (int i = 32; i < 128; i++) { + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + fprintf(stderr, "Loading character %c failed!\n", i); + continue; + } + + if (ox + g->bitmap.width + 1 >= MAXWIDTH) { + oy += rowh; + rowh = 0; + ox = 0; + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, g->bitmap.width, g->bitmap.rows, monohromeformat, GL_UNSIGNED_BYTE, g->bitmap.buffer); + c[i].ax = _FIXED2FLOAT(g->advance.x, 6); + c[i].ay = _FIXED2FLOAT(g->advance.y, 6); + + c[i].bw = (float)g->bitmap.width; + c[i].bh = (float)g->bitmap.rows; + + c[i].bl = (float)g->bitmap_left; + c[i].bt = (float)g->bitmap_top; + + c[i].tx = ox / (float)w; + c[i].ty = oy / (float)h; + + rowh = std::max(rowh, (int)g->bitmap.rows); + ox += g->bitmap.width + 1; + } + + fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024); + } + + ~Atlas() { + gfxContext.deleteTexture(graphics::ObjectHandle(tex)); + } +}; + +TextDrawer::TextDrawer() : + m_pAtlas(nullptr), m_program(0), m_uTex(0), m_uColor(0), m_vbo(0) +{} + +TextDrawer & TextDrawer::get() { + static TextDrawer drawer; + return drawer; +} + +static +bool getFontFileName(char * _strName) +{ +#ifdef OS_WINDOWS + char * pSysPath = getenv("WINDIR"); + if (pSysPath == nullptr) + return false; + sprintf(_strName, "%s/Fonts/%s", pSysPath, config.font.name.c_str()); +#elif defined (ANDROID) + sprintf(_strName, "/system/fonts/%s", config.font.name.c_str()); +#elif defined (PANDORA) + sprintf(_strName, "/usr/share/fonts/truetype/%s", config.font.name.c_str()); +#else + sprintf(_strName, "/usr/share/fonts/truetype/freefont/%s", config.font.name.c_str()); +#endif + return true; +} + +FT_Library ft; +FT_Face face; + +void TextDrawer::init() +{ + if (m_pAtlas != nullptr) + return; + + char strBuffer[PLUGIN_PATH_SIZE]; + const char *fontfilename; + if (getFontFileName(strBuffer)) + fontfilename = strBuffer; + else + return; + + /* Initialize the FreeType2 library */ + if (FT_Init_FreeType(&ft)) { + fprintf(stderr, "Could not init freetype library\n"); + return; + } + + /* Load a font */ + if (FT_New_Face(ft, fontfilename, 0, &face)) { + fprintf(stderr, "Could not open font %s\n", fontfilename); + return; + } + + m_program = createRectShaderProgram(strDrawTextVertexShader, strDrawTextFragmentShader); + if(m_program == 0) + return; + + m_uTex = glGetUniformLocation(m_program, "uTex"); + m_uColor = glGetUniformLocation(m_program, "uColor"); + + if(m_uTex == -1 || m_uColor == -1) + return; + + // Create the vertex buffer object + glGenBuffers(1, &m_vbo); + + /* Create texture atlas for selected font size */ + m_pAtlas = new Atlas(face, config.font.size); +} + +void TextDrawer::destroy() +{ + if (m_pAtlas == nullptr) + return; + delete m_pAtlas; + m_pAtlas = nullptr; + glDeleteBuffers(1, &m_vbo); + m_vbo = 0; + glDeleteProgram(m_program); + m_program = 0; + FT_Done_Face(face); + FT_Done_FreeType(ft); +} + +/** + * Render text using the currently loaded font and currently set font size. + * Rendering starts at coordinates (x, y), z is always 0. + * The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy). + */ +void TextDrawer::renderText(const char *_pText, float _x, float _y) const +{ + if (m_pAtlas == nullptr) + return; + OGLVideo & ogl = video(); + const float sx = 2.0f / ogl.getWidth(); + const float sy = 2.0f / ogl.getHeight(); + + const u8 *p; + + glUseProgram(m_program); + + /* Enable blending, necessary for our alpha texture */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + /* Set color */ + glUniform4fv(m_uColor, 1, config.font.colorf); + + /* Use the texture containing the atlas */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_pAtlas->tex); + glUniform1i(m_uTex, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); +#endif + + /* Set up the VBO for our vertex data */ + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + glVertexAttribPointer(SC_RECT_POSITION, 4, GL_FLOAT, GL_FALSE, 0, 0); + + std::vector coords(6 * strlen(_pText)); + int c = 0; + + /* Loop through all characters */ + for (p = (const u8 *)_pText; *p; ++p) { + /* Calculate the vertex and texture coordinates */ + float x2 = _x + m_pAtlas->c[*p].bl * sx; + float y2 = -_y - m_pAtlas->c[*p].bt * sy; + float w = m_pAtlas->c[*p].bw * sx; + float h = m_pAtlas->c[*p].bh * sy; + + /* Advance the cursor to the start of the next character */ + _x += m_pAtlas->c[*p].ax * sx; + _y += m_pAtlas->c[*p].ay * sy; + + /* Skip glyphs that have no pixels */ + if (!w || !h) + continue; + + coords[c++] = point(x2, -y2, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty); + coords[c++] = point(x2 + w, -y2, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty); + coords[c++] = point(x2, -y2 - h, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); + coords[c++] = point(x2 + w, -y2, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty); + coords[c++] = point(x2, -y2 - h, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); + coords[c++] = point(x2 + w, -y2 - h, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); + } + + /* Draw all the character on the screen in one go */ + glBufferData(GL_ARRAY_BUFFER, coords.size()*sizeof(point), coords.data(), GL_DYNAMIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, c); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void TextDrawer::getTextSize(const char *_pText, float & _w, float & _h) const +{ + _w = _h = 0; + if (m_pAtlas == nullptr) + return; + OGLVideo & ogl = video(); + const float sx = 2.0f / ogl.getWidth(); + const float sy = 2.0f / ogl.getHeight(); + float bw, bh; + + for (const u8 *p = (const u8 *)_pText; *p; ++p) { + bw = m_pAtlas->c[*p].bw * sx; + bh = m_pAtlas->c[*p].bh * sy; + + _w += m_pAtlas->c[*p].ax * sx; + _h += m_pAtlas->c[*p].ay * sy; + } + _w += bw; + _h += bh; +} diff --git a/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.cpp b/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.cpp index 408ace20..83fce74c 100644 --- a/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.cpp +++ b/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.cpp @@ -1,3 +1,4 @@ +#include #include "GLFunctions.h" #include "opengl_Attributes.h" #include "opengl_CachedFunctions.h" @@ -5,8 +6,9 @@ using namespace opengl; -UnbufferedDrawer::UnbufferedDrawer(CachedVertexAttribArray * _cachedAttribArray) -: m_cachedAttribArray(_cachedAttribArray) +UnbufferedDrawer::UnbufferedDrawer(const GLInfo & _glinfo, CachedVertexAttribArray * _cachedAttribArray) +: m_glInfo(_glinfo) +, m_cachedAttribArray(_cachedAttribArray) { m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::position, false); m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::color, false); @@ -37,6 +39,9 @@ bool UnbufferedDrawer::_updateAttribPointer(u32 _index, const void * _ptr) void UnbufferedDrawer::drawTriangles(const DrawTriangleParameters & _params) { + if (m_glInfo.imageTextures && config.frameBufferEmulation.N64DepthCompare != 0) + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + { m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::position, true); const void * ptr = &_params.vertices->x; @@ -100,7 +105,7 @@ void UnbufferedDrawer::drawRects(const DrawRectParameters & _params) { m_cachedAttribArray->enableVertexAttribArray(rectAttrib::color, true); - glVertexAttrib4fv(rectAttrib::color, _params.rectColor); + glVertexAttrib4fv(rectAttrib::color, _params.rectColor.data()); } if (_params.combiner->usesTile(0)) { @@ -127,3 +132,32 @@ void UnbufferedDrawer::drawRects(const DrawRectParameters & _params) glDrawArrays(GLenum(_params.mode), 0, _params.verticesCount); } + +void UnbufferedDrawer::drawLine(f32 _width, SPVertex * _vertices) +{ + { + m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::position, true); + const void * ptr = &_vertices->x; + if (_updateAttribPointer(triangleAttrib::position, ptr)) + glVertexAttribPointer(triangleAttrib::position, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), ptr); + } + + { + m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::color, true); + const void * ptr = &_vertices->r; + if (_updateAttribPointer(triangleAttrib::color, ptr)) + glVertexAttribPointer(triangleAttrib::color, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), ptr); + } + + m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::texcoord, false); + m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::numlights, false); + m_cachedAttribArray->enableVertexAttribArray(triangleAttrib::modify, false); + + m_cachedAttribArray->enableVertexAttribArray(rectAttrib::position, false); + m_cachedAttribArray->enableVertexAttribArray(rectAttrib::color, false); + m_cachedAttribArray->enableVertexAttribArray(rectAttrib::texcoord0, false); + m_cachedAttribArray->enableVertexAttribArray(rectAttrib::texcoord1, false); + + glLineWidth(_width); + glDrawArrays(GL_LINES, 0, 2); +} diff --git a/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.h b/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.h index 0ee9a2c4..3ced37d9 100644 --- a/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.h +++ b/src/Graphics/OpenGLContext/opengl_UnbufferedDrawer.h @@ -1,6 +1,7 @@ #pragma once #include -#include +#include +#include "opengl_GLInfo.h" namespace opengl { class CachedVertexAttribArray; @@ -8,16 +9,19 @@ namespace opengl { class UnbufferedDrawer : public graphics::DrawerImpl { public: - UnbufferedDrawer(CachedVertexAttribArray * _cachedAttribArray); + UnbufferedDrawer(const GLInfo & _glinfo, CachedVertexAttribArray * _cachedAttribArray); ~UnbufferedDrawer(); void drawTriangles(const DrawTriangleParameters & _params) override; void drawRects(const DrawRectParameters & _params) override; + void drawLine(f32 _width, SPVertex * _vertices) override; + private: bool _updateAttribPointer(u32 _index, const void * _ptr); + const GLInfo & m_glInfo; CachedVertexAttribArray * m_cachedAttribArray; std::array m_attribsData; }; diff --git a/src/Graphics/TextDrawerImpl.h b/src/Graphics/TextDrawerImpl.h new file mode 100644 index 00000000..75f02773 --- /dev/null +++ b/src/Graphics/TextDrawerImpl.h @@ -0,0 +1,16 @@ +#ifndef TEXTDRAWERIMPL_H +#define TEXTDRAWERIMPL_H + +namespace graphics { + + class TextDrawer + { + public: + virtual ~TextDrawer() {} + virtual void renderText(const char *_pText, float x, float y) const = 0; + virtual void getTextSize(const char *_pText, float & _w, float & _h) const = 0; + }; + +} + +#endif // TEXTDRAWERIMPL_H diff --git a/src/GraphicsDrawer.cpp b/src/GraphicsDrawer.cpp index 38cce715..e6196e92 100644 --- a/src/GraphicsDrawer.cpp +++ b/src/GraphicsDrawer.cpp @@ -4,13 +4,15 @@ #include "Graphics/Parameters.h" #include "Config.h" #include "RSP.h" +#include "RDP.h" #include "VI.h" #include "FrameBuffer.h" #include "DepthBuffer.h" #include "DisplayWindow.h" #include "SoftwareRender.h" -#include "Graphics/DrawerImpl.h" +#include "Graphics/GraphicsDrawerImpl.h" #include "GraphicsDrawer.h" +#include "Performance.h" using namespace graphics; @@ -19,6 +21,7 @@ GraphicsDrawer::GraphicsDrawer() , m_bImageTexture(false) , m_bFlatColors(false) , m_drawerImpl(gfxContext.createDrawerImpl()) +, m_textDrawer(gfxContext.createTextDrawer()) { } @@ -600,12 +603,9 @@ void GraphicsDrawer::_updateStates(DrawingState _drawingState) const } } -void GraphicsDrawer::_prepareDrawTriangle(bool _dma) +void GraphicsDrawer::_prepareDrawTriangle() { -#ifdef GL_IMAGE_TEXTURES_SUPPORT - if (m_bImageTexture && config.frameBufferEmulation.N64DepthCompare != 0) - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); -#endif // GL_IMAGE_TEXTURES_SUPPORT + m_texrectDrawer.draw(); if ((m_modifyVertices & MODIFY_XY) != 0) gSP.changed &= ~CHANGED_VIEWPORT; @@ -614,7 +614,6 @@ void GraphicsDrawer::_prepareDrawTriangle(bool _dma) _updateStates(DrawingState::Triangle); m_drawingState = DrawingState::Triangle; - m_bDmaVertices = _dma; bool bFlatColors = false; if (!RSP.bLLE && (gSP.geometryMode & G_LIGHTING) == 0) { @@ -641,17 +640,18 @@ void GraphicsDrawer::drawTriangles() return; } - _prepareDrawTriangle(false); + _prepareDrawTriangle(); - DrawerImpl::DrawTriangleParameters triPapams; - triPapams.mode = drawmode::TRIANGLES; - triPapams.elementsType = datatype::UNSIGNED_BYTE; - triPapams.verticesCount = static_cast(triangles.maxElement) + 1; - triPapams.elementsCount = triangles.num; - triPapams.vertices = triangles.vertices.data(); - triPapams.elements = triangles.elements.data(); - triPapams.combiner = currentCombiner(); - m_drawerImpl->drawTriangles(triPapams); + DrawerImpl::DrawTriangleParameters triParams; + triParams.mode = drawmode::TRIANGLES; + triParams.flatColors = m_bFlatColors; + triParams.elementsType = datatype::UNSIGNED_BYTE; + triParams.verticesCount = static_cast(triangles.maxElement) + 1; + triParams.elementsCount = triangles.num; + triParams.vertices = triangles.vertices.data(); + triParams.elements = triangles.elements.data(); + triParams.combiner = currentCombiner(); + m_drawerImpl->drawTriangles(triParams); if (config.frameBufferEmulation.enable != 0 && config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && @@ -665,3 +665,730 @@ void GraphicsDrawer::drawTriangles() triangles.num = 0; triangles.maxElement = 0; } + +void GraphicsDrawer::drawScreenSpaceTriangle(u32 _numVtx) +{ + if (_numVtx == 0 || !_canDraw()) + return; + + for (u32 i = 0; i < _numVtx; ++i) { + SPVertex & vtx = m_dmaVertices[i]; + vtx.modify = MODIFY_ALL; + } + m_modifyVertices = MODIFY_ALL; + + gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode + _prepareDrawTriangle(); + gfxContext.enable(enable::CULL_FACE, false); + + DrawerImpl::DrawTriangleParameters triParams; + triParams.mode = drawmode::TRIANGLE_STRIP; + triParams.flatColors = m_bFlatColors; + triParams.verticesCount = _numVtx; + triParams.vertices = m_dmaVertices.data(); + triParams.combiner = currentCombiner(); + m_drawerImpl->drawTriangles(triParams); + + frameBufferList().setBufferChanged(); + gSP.changed |= CHANGED_GEOMETRYMODE; +} + +void GraphicsDrawer::drawDMATriangles(u32 _numVtx) +{ + if (_numVtx == 0 || !_canDraw()) + return; + _prepareDrawTriangle(); + + + DrawerImpl::DrawTriangleParameters triParams; + triParams.mode = drawmode::TRIANGLES; + triParams.flatColors = m_bFlatColors; + triParams.verticesCount = _numVtx; + triParams.vertices = m_dmaVertices.data(); + triParams.combiner = currentCombiner(); + m_drawerImpl->drawTriangles(triParams); + + if (config.frameBufferEmulation.enable != 0 && + config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && + gDP.otherMode.depthUpdate != 0) { + renderTriangles(m_dmaVertices.data(), nullptr, _numVtx); + FrameBuffer * pCurrentDepthBuffer = frameBufferList().findBuffer(gDP.depthImageAddress); + if (pCurrentDepthBuffer != nullptr) + pCurrentDepthBuffer->m_cleared = false; + } +} + +void GraphicsDrawer::_drawThickLine(int _v0, int _v1, float _width) +{ + if ((gSP.geometryMode & G_LIGHTING) == 0) { + if ((gSP.geometryMode & G_SHADE) == 0) { + SPVertex & vtx1 = triangles.vertices[_v0]; + vtx1.flat_r = gDP.primColor.r; + vtx1.flat_g = gDP.primColor.g; + vtx1.flat_b = gDP.primColor.b; + vtx1.flat_a = gDP.primColor.a; + SPVertex & vtx2 = triangles.vertices[_v1]; + vtx2.flat_r = gDP.primColor.r; + vtx2.flat_g = gDP.primColor.g; + vtx2.flat_b = gDP.primColor.b; + vtx2.flat_a = gDP.primColor.a; + } + else if ((gSP.geometryMode & G_SHADING_SMOOTH) == 0) { + // Flat shading + SPVertex & vtx0 = triangles.vertices[_v0 + ((RSP.w1 >> 24) & 3)]; + SPVertex & vtx1 = triangles.vertices[_v0]; + vtx1.r = vtx1.flat_r = vtx0.r; + vtx1.g = vtx1.flat_g = vtx0.g; + vtx1.b = vtx1.flat_b = vtx0.b; + vtx1.a = vtx1.flat_a = vtx0.a; + SPVertex & vtx2 = triangles.vertices[_v1]; + vtx2.r = vtx2.flat_r = vtx0.r; + vtx2.g = vtx2.flat_g = vtx0.g; + vtx2.b = vtx2.flat_b = vtx0.b; + vtx2.a = vtx2.flat_a = vtx0.a; + } + } + + setDMAVerticesSize(4); + SPVertex * pVtx = getDMAVerticesData(); + pVtx[0] = triangles.vertices[_v0]; + pVtx[0].x = pVtx[0].x / pVtx[0].w * gSP.viewport.vscale[0] + gSP.viewport.vtrans[0]; + pVtx[0].y = pVtx[0].y / pVtx[0].w * gSP.viewport.vscale[1] + gSP.viewport.vtrans[1]; + pVtx[0].z = pVtx[0].z / pVtx[0].w * gSP.viewport.vscale[2] + gSP.viewport.vtrans[2]; + pVtx[1] = pVtx[0]; + + pVtx[2] = triangles.vertices[_v1]; + pVtx[2].x = pVtx[2].x / pVtx[2].w * gSP.viewport.vscale[0] + gSP.viewport.vtrans[0]; + pVtx[2].y = pVtx[2].y / pVtx[2].w * gSP.viewport.vscale[1] + gSP.viewport.vtrans[1]; + pVtx[2].z = pVtx[2].z / pVtx[2].w * gSP.viewport.vscale[2] + gSP.viewport.vtrans[2]; + pVtx[3] = pVtx[2]; + + if (fabs(pVtx[0].y - pVtx[2].y) < 0.0001) { + const f32 Y = pVtx[0].y; + pVtx[0].y = pVtx[2].y = Y - _width; + pVtx[1].y = pVtx[3].y = Y + _width; + } + else if (fabs(pVtx[0].x - pVtx[2].x) < 0.0001) { + const f32 X = pVtx[0].x; + pVtx[0].x = pVtx[2].x = X - _width; + pVtx[1].x = pVtx[3].x = X + _width; + } + else { + const f32 X0 = pVtx[0].x; + const f32 Y0 = pVtx[0].y; + const f32 X1 = pVtx[2].x; + const f32 Y1 = pVtx[2].y; + const f32 dx = X1 - X0; + const f32 dy = Y1 - Y0; + const f32 len = sqrtf(dx*dx + dy*dy); + const f32 wx = dy * _width / len; + const f32 wy = dx * _width / len; + pVtx[0].x = X0 + wx; + pVtx[0].y = Y0 - wy; + pVtx[1].x = X0 - wx; + pVtx[1].y = Y0 + wy; + pVtx[2].x = X1 + wx; + pVtx[2].y = Y1 - wy; + pVtx[3].x = X1 - wx; + pVtx[3].y = Y1 + wy; + } + drawScreenSpaceTriangle(4); +} + +void GraphicsDrawer::drawLine(int _v0, int _v1, float _width) +{ + m_texrectDrawer.draw(); + + if (!_canDraw()) + return; + + GLfloat lineWidth = _width; + if (config.frameBufferEmulation.nativeResFactor == 0) + lineWidth *= video().getScaleX(); + else + lineWidth *= config.frameBufferEmulation.nativeResFactor; + if (lineWidth > m_maxLineWidth) { + _drawThickLine(_v0, _v1, _width * 0.5f); + return; + } + + if ((triangles.vertices[_v0].modify & MODIFY_XY) != 0) + gSP.changed &= ~CHANGED_VIEWPORT; + if (gSP.changed || gDP.changed) + _updateStates(DrawingState::Line); + + m_drawingState = DrawingState::Line; + + if ((triangles.vertices[_v0].modify & MODIFY_XY) != 0) + _updateScreenCoordsViewport(); + + SPVertex vertexBuf[2] = { triangles.vertices[triangles.elements[_v0]], triangles.vertices[triangles.elements[_v1]] }; + m_drawerImpl->drawLine(lineWidth, vertexBuf); +} + +void GraphicsDrawer::drawRect(int _ulx, int _uly, int _lrx, int _lry, float *_pColor) +{ + m_texrectDrawer.draw(); + + if (!_canDraw()) + return; + + gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode + if (gSP.changed || gDP.changed) + _updateStates(DrawingState::Rect); + + m_drawingState = DrawingState::Rect; + + FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent(); + OGLVideo & ogl = video(); + if (pCurrentBuffer == nullptr) + gfxContext.setViewport(0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight()); + else + gfxContext.setViewport(0, 0, pCurrentBuffer->m_width*pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height*pCurrentBuffer->m_scaleY); + + gfxContext.enable(enable::CULL_FACE, false); + + const float scaleX = pCurrentBuffer != nullptr ? 1.0f / pCurrentBuffer->m_width : VI.rwidth; + const float scaleY = pCurrentBuffer != nullptr ? 1.0f / pCurrentBuffer->m_height : VI.rheight; + const float Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : 0.0f; + const float W = 1.0f; + m_rect[0].x = (float)_ulx * (2.0f * scaleX) - 1.0; + m_rect[0].y = (float)_uly * (-2.0f * scaleY) + 1.0; + m_rect[0].z = Z; + m_rect[0].w = W; + m_rect[1].x = (float)_lrx * (2.0f * scaleX) - 1.0; + m_rect[1].y = m_rect[0].y; + m_rect[1].z = Z; + m_rect[1].w = W; + m_rect[2].x = m_rect[0].x; + m_rect[2].y = (float)_lry * (-2.0f * scaleY) + 1.0; + m_rect[2].z = Z; + m_rect[2].w = W; + m_rect[3].x = m_rect[1].x; + m_rect[3].y = m_rect[2].y; + m_rect[3].z = Z; + m_rect[3].w = W; + + if (ogl.isAdjustScreen() && (gDP.colorImage.width > VI.width * 98 / 100) && (_lrx - _ulx < VI.width * 9 / 10)) { + const float scale = ogl.getAdjustScale(); + for (u32 i = 0; i < 4; ++i) + m_rect[i].x *= scale; + } + + graphics::DrawerImpl::DrawRectParameters rectParams; + rectParams.mode = drawmode::TRIANGLE_STRIP; + if (gDP.otherMode.cycleType == G_CYC_FILL) + std::copy_n(_pColor, sizeof(_pColor[0]) * 4, rectParams.rectColor.data()); + else + rectParams.rectColor.fill(0.0f); + rectParams.verticesCount = 4; + rectParams.vertices = m_rect; + rectParams.combiner = currentCombiner(); + m_drawerImpl->drawRects(rectParams); + gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT; +} + +static +bool texturedRectShadowMap(const GraphicsDrawer::TexturedRectParams &) +{ + FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent(); + if (pCurrentBuffer != nullptr) { + if (gDP.textureImage.size == 2 && gDP.textureImage.address >= gDP.depthImageAddress && gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4)) { +#ifdef GL_IMAGE_TEXTURES_SUPPORT + if (video().getRender().isImageTexturesSupported()) { + pCurrentBuffer->m_pDepthBuffer->activateDepthBufferTexture(pCurrentBuffer); + CombinerInfo::get().setDepthFogCombiner(); + } + else + return true; +#else + return true; +#endif + } + } + return false; +} + +//u32 rectDepthBufferCopyFrame = 0xFFFFFFFF; +static +bool texturedRectDepthBufferCopy(const GraphicsDrawer::TexturedRectParams & _params) +{ + // Copy one line from depth buffer into auxiliary color buffer with height = 1. + // Data from depth buffer loaded into TMEM and then rendered to RDRAM by texrect. + // Works only with depth buffer emulation enabled. + // Load of arbitrary data to that area causes weird camera rotation in CBFD. + const gDPTile * pTile = gSP.textureTile[0]; + if (pTile->loadType == LOADTYPE_BLOCK && gDP.textureImage.size == 2 && gDP.textureImage.address >= gDP.depthImageAddress && gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4)) { + if (config.frameBufferEmulation.copyDepthToRDRAM == Config::cdDisable) + return true; + FrameBuffer * pBuffer = frameBufferList().getCurrent(); + if (pBuffer == nullptr) + return true; + pBuffer->m_cleared = true; + if (config.frameBufferEmulation.copyDepthToRDRAM == Config::cdCopyFromVRam) { + if (rectDepthBufferCopyFrame != video().getBuffersSwapCount()) { + rectDepthBufferCopyFrame = video().getBuffersSwapCount(); + if (!FrameBuffer_CopyDepthBuffer(gDP.colorImage.address)) + return true; + } + RDP_RepeatLastLoadBlock(); + } + + const u32 width = (u32)(_params.lrx - _params.ulx); + const u32 ulx = (u32)_params.ulx; + u16 * pSrc = ((u16*)TMEM) + (u32)floorf(_params.uls + 0.5f); + u16 *pDst = (u16*)(RDRAM + gDP.colorImage.address); + for (u32 x = 0; x < width; ++x) + pDst[(ulx + x) ^ 1] = swapword(pSrc[x]); + + return true; + } + return false; +} + +static +bool texturedRectCopyToItself(const GraphicsDrawer::TexturedRectParams & _params) +{ + FrameBuffer * pCurrent = frameBufferList().getCurrent(); + if (pCurrent != nullptr && pCurrent->m_size == G_IM_SIZ_8b && gSP.textureTile[0]->frameBuffer == pCurrent) + return true; + return texturedRectDepthBufferCopy(_params); +} + +static +bool texturedRectBGCopy(const GraphicsDrawer::TexturedRectParams & _params) +{ + if (GBI.getMicrocodeType() != S2DEX) + return false; + + float flry = _params.lry; + if (flry > gDP.scissor.lry) + flry = gDP.scissor.lry; + + const u32 width = (u32)(_params.lrx - _params.ulx); + const u32 tex_width = gSP.textureTile[0]->line << 3; + const u32 uly = (u32)_params.uly; + const u32 lry = flry; + + u8 * texaddr = RDRAM + gDP.loadInfo[gSP.textureTile[0]->tmem].texAddress + tex_width*(u32)_params.ult + (u32)_params.uls; + u8 * fbaddr = RDRAM + gDP.colorImage.address + (u32)_params.ulx; + // LOG(LOG_VERBOSE, "memrect (%d, %d, %d, %d), ci_width: %d texaddr: 0x%08lx fbaddr: 0x%08lx\n", (u32)_params.ulx, uly, (u32)_params.lrx, lry, gDP.colorImage.width, gSP.textureTile[0]->imageAddress + tex_width*(u32)_params.ult + (u32)_params.uls, gDP.colorImage.address + (u32)_params.ulx); + + for (u32 y = uly; y < lry; ++y) { + u8 *src = texaddr + (y - uly) * tex_width; + u8 *dst = fbaddr + y * gDP.colorImage.width; + memcpy(dst, src, width); + } + frameBufferList().removeBuffer(gDP.colorImage.address); + return true; +} + +static +bool texturedRectPaletteMod(const GraphicsDrawer::TexturedRectParams & _params) +{ + if (gDP.textureImage.address == 0x400) { + // Paper Mario uses complex set of actions to prepare darkness texture. + // It includes manipulations with texture formats and drawing buffer into itsels. + // All that stuff is hardly possible to reproduce with GL, so I just use dirty hacks to emualte it. + + if (gDP.colorImage.address == 0x400 && gDP.colorImage.width == 64) { + memcpy(RDRAM + 0x400, RDRAM + 0x14d500, 4096); + return true; + } + + if (gDP.textureImage.width == 64) { + gDPTile & curTile = gDP.tiles[0]; + curTile.frameBuffer = nullptr; + curTile.textureMode = TEXTUREMODE_NORMAL; + textureCache().update(0); + currentCombiner()->update(false); + } + return false; + } + + // Modify palette for Paper Mario "2D lighting" effect + if (gDP.scissor.lrx != 16 || gDP.scissor.lry != 1 || _params.lrx != 16 || _params.lry != 1) + return false; + u8 envr = (u8)(gDP.envColor.r * 31.0f); + u8 envg = (u8)(gDP.envColor.g * 31.0f); + u8 envb = (u8)(gDP.envColor.b * 31.0f); + u16 env16 = (u16)((envr << 11) | (envg << 6) | (envb << 1) | 1); + u8 prmr = (u8)(gDP.primColor.r * 31.0f); + u8 prmg = (u8)(gDP.primColor.g * 31.0f); + u8 prmb = (u8)(gDP.primColor.b * 31.0f); + u16 prim16 = (u16)((prmr << 11) | (prmg << 6) | (prmb << 1) | 1); + u16 * src = (u16*)&TMEM[256]; + u16 * dst = (u16*)(RDRAM + gDP.colorImage.address); + for (u32 i = 0; i < 16; ++i) + dst[i ^ 1] = (src[i << 2] & 0x100) ? prim16 : env16; + return true; +} + +static +bool texturedRectMonochromeBackground(const GraphicsDrawer::TexturedRectParams & _params) +{ + if (gDP.textureImage.address >= gDP.colorImage.address && gDP.textureImage.address <= (gDP.colorImage.address + gDP.colorImage.width*gDP.colorImage.height * 2)) { +#ifdef GL_IMAGE_TEXTURES_SUPPORT + FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent(); + if (pCurrentBuffer != nullptr) { + FrameBuffer_ActivateBufferTexture(0, pCurrentBuffer); + CombinerInfo::get().setMonochromeCombiner(); + return false; + } + else +#endif + return true; + } + return false; +} + +// Special processing of textured rect. +// Return true if actuial rendering is not necessary +bool(*texturedRectSpecial)(const GraphicsDrawer::TexturedRectParams & _params) = nullptr; + +void GraphicsDrawer::drawTexturedRect(const TexturedRectParams & _params) +{ + gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode + m_drawingState = DrawingState::TexRect; + f32 alpha = 0.0f; + + if (!m_texrectDrawer.isEmpty()) { + CombinerInfo & cmbInfo = CombinerInfo::get(); + cmbInfo.setPolygonMode(DrawingState::TexRect); + cmbInfo.update(); + _updateTextures(); + cmbInfo.updateParameters(); + } + else { + if (_params.texrectCmd && (gSP.changed | gDP.changed) != 0) + _updateStates(DrawingState::TexRect); + glDisable(GL_CULL_FACE); + + if (CombinerInfo::get().isChanged()) { + if (currentCombiner()->usesShade()) { + gDPCombine combine; + combine.mux = currentCombiner()->getKey().getMux(); + if (combine.mA0 == G_ACMUX_0 && combine.aA0 == G_ACMUX_SHADE) + alpha = 1.0f; + } + } + + if (_params.texrectCmd && texturedRectSpecial != nullptr && texturedRectSpecial(_params)) { + gSP.changed |= CHANGED_GEOMETRYMODE; + return; + } + + if (!_canDraw()) + return; + } + + CombinerProgram * pCurrentCombiner = currentCombiner(); + const FrameBuffer * pCurrentBuffer = _params.pBuffer; + OGLVideo & ogl = video(); + TextureCache & cache = textureCache(); + const bool bUseBilinear = (gDP.otherMode.textureFilter | (gSP.objRendermode&G_OBJRM_BILERP)) != 0; + const bool bUseTexrectDrawer = config.generalEmulation.enableNativeResTexrects != 0 + && bUseBilinear + && pCurrentCombiner->usesTexture() + && (pCurrentBuffer == nullptr || !pCurrentBuffer->m_cfb) + && (cache.current[0] != nullptr) + // && (cache.current[0] == nullptr || cache.current[0]->format == G_IM_FMT_RGBA || cache.current[0]->format == G_IM_FMT_CI) + && ((cache.current[0]->frameBufferTexture == CachedTexture::fbNone && !cache.current[0]->bHDTexture)) + && (cache.current[1] == nullptr || (cache.current[1]->frameBufferTexture == CachedTexture::fbNone && !cache.current[1]->bHDTexture)); + + const float scaleX = pCurrentBuffer != nullptr ? 1.0f / pCurrentBuffer->m_width : VI.rwidth; + const float scaleY = pCurrentBuffer != nullptr ? 1.0f / pCurrentBuffer->m_height : VI.rheight; + const float Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : 0.0f; + const float W = 1.0f; + f32 uly, lry; + if (bUseTexrectDrawer) { + uly = (float)_params.uly * (2.0f * scaleY) - 1.0f; + lry = (float)_params.lry * (2.0f * scaleY) - 1.0f; + } + else { + uly = (float)_params.uly * (-2.0f * scaleY) + 1.0f; + lry = (float)(_params.lry) * (-2.0f * scaleY) + 1.0f; + // Flush text drawer + if (m_texrectDrawer.draw()) + _updateStates(DrawingState::TexRect); + } + m_rect[0].x = (float)_params.ulx * (2.0f * scaleX) - 1.0f; + m_rect[0].y = uly; + m_rect[0].z = Z; + m_rect[0].w = W; + m_rect[1].x = (float)(_params.lrx) * (2.0f * scaleX) - 1.0f; + m_rect[1].y = m_rect[0].y; + m_rect[1].z = Z; + m_rect[1].w = W; + m_rect[2].x = m_rect[0].x; + m_rect[2].y = lry; + m_rect[2].z = Z; + m_rect[2].w = W; + m_rect[3].x = m_rect[1].x; + m_rect[3].y = m_rect[2].y; + m_rect[3].z = Z; + m_rect[3].w = W; + + struct + { + float s0, t0, s1, t1; + } texST[2] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; //struct for texture coordinates + + for (u32 t = 0; t < 2; ++t) { + if (pCurrentCombiner->usesTile(t) && cache.current[t] && gSP.textureTile[t]) { + f32 shiftScaleS = 1.0f; + f32 shiftScaleT = 1.0f; + getTextureShiftScale(t, cache, shiftScaleS, shiftScaleT); + if (_params.uls > _params.lrs) { + texST[t].s0 = (_params.uls + _params.dsdx) * shiftScaleS - gSP.textureTile[t]->fuls; + texST[t].s1 = _params.lrs * shiftScaleS - gSP.textureTile[t]->fuls; + } + else { + texST[t].s0 = _params.uls * shiftScaleS - gSP.textureTile[t]->fuls; + texST[t].s1 = (_params.lrs + _params.dsdx) * shiftScaleS - gSP.textureTile[t]->fuls; + } + if (_params.ult > _params.lrt) { + texST[t].t0 = (_params.ult + _params.dtdy) * shiftScaleT - gSP.textureTile[t]->fult; + texST[t].t1 = _params.lrt * shiftScaleT - gSP.textureTile[t]->fult; + } + else { + texST[t].t0 = _params.ult * shiftScaleT - gSP.textureTile[t]->fult; + texST[t].t1 = (_params.lrt + _params.dtdy) * shiftScaleT - gSP.textureTile[t]->fult; + } + + if (cache.current[t]->frameBufferTexture != CachedTexture::fbNone) { + texST[t].s0 = cache.current[t]->offsetS + texST[t].s0; + texST[t].t0 = cache.current[t]->offsetT - texST[t].t0; + texST[t].s1 = cache.current[t]->offsetS + texST[t].s1; + texST[t].t1 = cache.current[t]->offsetT - texST[t].t1; + } + + Context::TexParameters texParams; + + if ((cache.current[t]->mirrorS == 0 && cache.current[t]->maskS == 0 && + (texST[t].s0 < texST[t].s1 ? + texST[t].s0 >= 0.0 && texST[t].s1 <= (float)cache.current[t]->width : + texST[t].s1 >= 0.0 && texST[t].s0 <= (float)cache.current[t]->width)) + || (cache.current[t]->maskS == 0 && (texST[t].s0 < -1024.0f || texST[t].s1 > 1023.99f))) + texParams.wrapS = textureParameters::WRAP_CLAMP_TO_EDGE; + + if (cache.current[t]->mirrorT == 0 && + (texST[t].t0 < texST[t].t1 ? + texST[t].t0 >= 0.0f && texST[t].t1 <= (float)cache.current[t]->height : + texST[t].t1 >= 0.0f && texST[t].t0 <= (float)cache.current[t]->height)) + texParams.wrapT = textureParameters::WRAP_CLAMP_TO_EDGE; + + if (texParams.wrapS.isValid() || texParams.wrapT.isValid()) { + texParams.handle = ObjectHandle(cache.current[t]->glName); + texParams.textureUnitIndex = textureIndices::Tex[t]; + gfxContext.setTextureParameters(texParams); + } + + texST[t].s0 *= cache.current[t]->scaleS; + texST[t].t0 *= cache.current[t]->scaleT; + texST[t].s1 *= cache.current[t]->scaleS; + texST[t].t1 *= cache.current[t]->scaleT; + } + } + + if (gDP.otherMode.cycleType == G_CYC_COPY) { + Context::TexParameters texParams; + texParams.handle = ObjectHandle(cache.current[0]->glName); + texParams.textureUnitIndex = textureIndices::Tex[0]; + texParams.minFilter = textureParameters::FILTER_NEAREST; + texParams.magFilter = textureParameters::FILTER_NEAREST; + gfxContext.setTextureParameters(texParams); + } + + m_rect[0].s0 = texST[0].s0; + m_rect[0].t0 = texST[0].t0; + m_rect[0].s1 = texST[1].s0; + m_rect[0].t1 = texST[1].t0; + + m_rect[3].s0 = texST[0].s1; + m_rect[3].t0 = texST[0].t1; + m_rect[3].s1 = texST[1].s1; + m_rect[3].t1 = texST[1].t1; + + if (_params.flip) { + m_rect[1].s0 = texST[0].s0; + m_rect[1].t0 = texST[0].t1; + m_rect[1].s1 = texST[1].s0; + m_rect[1].t1 = texST[1].t1; + + m_rect[2].s0 = texST[0].s1; + m_rect[2].t0 = texST[0].t0; + m_rect[2].s1 = texST[1].s1; + m_rect[2].t1 = texST[1].t0; + } + else { + m_rect[1].s0 = texST[0].s1; + m_rect[1].t0 = texST[0].t0; + m_rect[1].s1 = texST[1].s1; + m_rect[1].t1 = texST[1].t0; + + m_rect[2].s0 = texST[0].s0; + m_rect[2].t0 = texST[0].t1; + m_rect[2].s1 = texST[1].s0; + m_rect[2].t1 = texST[1].t1; + } + + if (ogl.isAdjustScreen() && + (_params.forceAjustScale || + ((gDP.colorImage.width > VI.width * 98 / 100) && (_params.lrx - _params.ulx < VI.width * 9 / 10)))) + { + const float scale = ogl.getAdjustScale(); + for (u32 i = 0; i < 4; ++i) + m_rect[i].x *= scale; + } + + if (bUseTexrectDrawer) + m_texrectDrawer.add(); + else { + if (pCurrentBuffer == nullptr) + gfxContext.setViewport(0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight()); + else + gfxContext.setViewport(0, 0, pCurrentBuffer->m_width*pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height*pCurrentBuffer->m_scaleY); + + graphics::DrawerImpl::DrawRectParameters rectParams; + rectParams.mode = drawmode::TRIANGLE_STRIP; + rectParams.rectColor.fill(0.0f); + rectParams.rectColor[3] = alpha; + rectParams.verticesCount = 4; + rectParams.vertices = m_rect; + rectParams.combiner = currentCombiner(); + m_drawerImpl->drawRects(rectParams); + + gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT; + } +} + +void GraphicsDrawer::correctTexturedRectParams(TexturedRectParams & _params) +{ + if (config.generalEmulation.correctTexrectCoords == Config::tcSmart) { + if (_params.ulx == m_texrectParams.ulx && _params.lrx == m_texrectParams.lrx) { + if (fabsf(_params.uly - m_texrectParams.lry) < 0.51f) + _params.uly = m_texrectParams.lry; + else if (fabsf(_params.lry - m_texrectParams.uly) < 0.51f) + _params.lry = m_texrectParams.uly; + } + else if (_params.uly == m_texrectParams.uly && _params.lry == m_texrectParams.lry) { + if (fabsf(_params.ulx - m_texrectParams.lrx) < 0.51f) + _params.ulx = m_texrectParams.lrx; + else if (fabsf(_params.lrx - m_texrectParams.ulx) < 0.51f) + _params.lrx = m_texrectParams.ulx; + } + } + else if (config.generalEmulation.correctTexrectCoords == Config::tcForce) { + _params.lrx += 0.25f; + _params.lry += 0.25f; + } + + m_texrectParams = _params; +} + +void GraphicsDrawer::drawText(const char *_pText, float x, float y) +{ + m_drawingState = DrawingState::None; + m_textDrawer->renderText(_pText, x, y); +} + +void GraphicsDrawer::_getTextSize(const char *_pText, float & _w, float & _h) const +{ + m_textDrawer->getTextSize(_pText, _w, _h); +} + +void GraphicsDrawer::_drawOSD(const char *_pText, float _x, float & _y) +{ + float tW, tH; + _getTextSize(_pText, tW, tH); + + const bool top = (config.posTop & config.onScreenDisplay.pos) != 0; + const bool right = (config.onScreenDisplay.pos == Config::posTopRight) || (config.onScreenDisplay.pos == Config::posBottomRight); + const bool center = (config.onScreenDisplay.pos == Config::posTopCenter) || (config.onScreenDisplay.pos == Config::posBottomCenter); + + if (center) + _x = -tW * 0.5f; + else if (right) + _x -= tW; + + if (top) + _y -= tH; + + drawText(_pText, _x, _y); + + if (top) + _y -= tH * 0.5f; + else + _y += tH * 1.5f; +} + +void GraphicsDrawer::drawOSD() +{ + if ((config.onScreenDisplay.fps | config.onScreenDisplay.vis | config.onScreenDisplay.percent) == 0) + return; + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + DisplayWindow & wnd = DisplayWindow::get(); + const GLint X = (wnd.getScreenWidth() - wnd.getWidth()) / 2; + const GLint Y = wnd.getHeightOffset(); + const GLint W = wnd.getWidth(); + const GLint H = wnd.getHeight(); + glViewport(X, Y, W, H); + glScissor(X, Y, W, H); + gSP.changed |= CHANGED_VIEWPORT; + gDP.changed |= CHANGED_SCISSOR; + + + const bool bottom = (config.posBottom & config.onScreenDisplay.pos) != 0; + const bool left = (config.onScreenDisplay.pos == Config::posTopLeft) || (config.onScreenDisplay.pos == Config::posBottomLeft); + + const float hp = left ? -1 : 1; + const float vp = bottom ? -1 : 1; + + float hShift, vShift; + _getTextSize("0", hShift, vShift); + hShift *= 0.5f; + vShift *= 0.5f; + const float x = hp - hShift * hp; + float y = vp - vShift * vp; + char buf[16]; + + if (config.onScreenDisplay.fps) { + sprintf(buf, "%d FPS", int(perf.getFps())); + _drawOSD(buf, x, y); + } + + if (config.onScreenDisplay.vis) { + sprintf(buf, "%d VI/S", int(perf.getVIs())); + _drawOSD(buf, x, y); + } + + if (config.onScreenDisplay.percent) { + sprintf(buf, "%d %%", int(perf.getPercent())); + _drawOSD(buf, x, y); + } + + frameBufferList().setCurrentDrawBuffer(); +} + +void GraphicsDrawer::clearDepthBuffer(u32 _ulx, u32 _uly, u32 _lrx, u32 _lry) +{ + if (!_canDraw()) + return; + + depthBufferList().clearBuffer(_ulx, _uly, _lrx, _lry); + + gfxContext.clearDepthBuffer(); + + _updateDepthUpdate(); +} + +void GraphicsDrawer::clearColorBuffer(float *_pColor) +{ + if (_pColor != nullptr) + gfxContext.clearColorBuffer(_pColor[0], _pColor[1], _pColor[2], _pColor[3]); + else + gfxContext.clearColorBuffer(0.0f, 0.0f, 0.0f, 0.0f); +} diff --git a/src/GraphicsDrawer.h b/src/GraphicsDrawer.h index 049fc121..2ab4823d 100644 --- a/src/GraphicsDrawer.h +++ b/src/GraphicsDrawer.h @@ -7,6 +7,7 @@ namespace graphics { class DrawerImpl; + class TextDrawer; } struct CachedTexture; @@ -134,7 +135,7 @@ private: void _updateDepthCompare() const; void _updateTextures() const; void _updateStates(DrawingState _drawingState) const; - void _prepareDrawTriangle(bool _dma); + void _prepareDrawTriangle(); bool _canDraw() const; void _drawThickLine(int _v0, int _v1, float _width); @@ -144,6 +145,18 @@ private: DrawingState m_drawingState; TexturedRectParams m_texrectParams; + // Dummy TexrectDrawer + class TexrectDrawer + { + public: + TexrectDrawer() {} + void init() {} + void destroy() {} + void add() {} + bool draw() { return false; } + bool isEmpty() { return true; } + }; + struct { std::array vertices; std::array elements; @@ -159,9 +172,10 @@ private: f32 m_maxLineWidth; bool m_bImageTexture; bool m_bFlatColors; - bool m_bDmaVertices; + TexrectDrawer m_texrectDrawer; std::unique_ptr m_drawerImpl; + std::unique_ptr m_textDrawer; //GLuint m_programCopyTex; };