diff --git a/include/math_util.hpp b/include/math_util.hpp new file mode 100644 index 00000000..f2b41f41 --- /dev/null +++ b/include/math_util.hpp @@ -0,0 +1,80 @@ +#pragma once +#include + +namespace Math { + // Abstraction for GLSL vectors + template + class Vector { + // A GLSL vector can only have 2, 3 or 4 elements + static_assert(size == 2 || size == 3 || size == 4); + T m_storage[size]; + + public: + T& r() { return m_storage[0]; } + T& g() { return m_storage[1]; } + T& b() { + static_assert(size >= 3, "Out of bounds OpenGL::Vector access"); + return m_storage[2]; + } + T& a() { + static_assert(size >= 4, "Out of bounds OpenGL::Vector access"); + return m_storage[3]; + } + + T& x() { return r(); } + T& y() { return g(); } + T& z() { return b(); } + T& w() { return a(); } + T& operator[](size_t index) { return m_storage[index]; } + const T& operator[](size_t index) const { return m_storage[index]; } + + T& u() { return r(); } + T& v() { return g(); } + + T& s() { return r(); } + T& t() { return g(); } + T& p() { return b(); } + T& q() { return a(); } + + Vector(std::array list) { std::copy(list.begin(), list.end(), &m_storage[0]); } + + Vector() {} + }; + + using vec2 = Vector; + using vec3 = Vector; + using vec4 = Vector; + + using dvec2 = Vector; + using dvec3 = Vector; + using dvec4 = Vector; + + using ivec2 = Vector; + using ivec3 = Vector; + using ivec4 = Vector; + + using uvec2 = Vector; + using uvec3 = Vector; + using uvec4 = Vector; + + // A 2D rectangle, meant to be used for stuff like scissor rects or viewport rects + // We're never supporting 3D rectangles, because rectangles were never meant to be 3D in the first place + template + struct Rectangle { + Vector start; + Vector end; + + Rectangle() : start({0}), end({0}) {} + Rectangle(T x0, T y0, T x1, T y1) : start({x0, y0}), end({x1, y1}) {} + + T getWidth() const { + return std::abs(end.x() - start.x()); + } + + T getHeight() const { + return std::abs(end.y() - start.y()); + } + }; + + using Rect = Rectangle; +} diff --git a/include/renderer_gl/opengl.hpp b/include/renderer_gl/opengl.hpp index e129f6b6..a2afab40 100644 --- a/include/renderer_gl/opengl.hpp +++ b/include/renderer_gl/opengl.hpp @@ -615,83 +615,4 @@ namespace OpenGL { glBlendFuncSeparate(fac1, fac2, fac3, fac4); } - // Abstraction for GLSL vectors - template - class Vector { - // A GLSL vector can only have 2, 3 or 4 elements - static_assert(size == 2 || size == 3 || size == 4); - T m_storage[size]; - - public: - T& r() { return m_storage[0]; } - T& g() { return m_storage[1]; } - T& b() { - static_assert(size >= 3, "Out of bounds OpenGL::Vector access"); - return m_storage[2]; - } - T& a() { - static_assert(size >= 4, "Out of bounds OpenGL::Vector access"); - return m_storage[3]; - } - - T& x() { return r(); } - T& y() { return g(); } - T& z() { return b(); } - T& w() { return a(); } - T& operator[](size_t index) { return m_storage[index]; } - const T& operator[](size_t index) const { return m_storage[index]; } - - T& u() { return r(); } - T& v() { return g(); } - - T& s() { return r(); } - T& t() { return g(); } - T& p() { return b(); } - T& q() { return a(); } - - Vector(std::array list) { std::copy(list.begin(), list.end(), &m_storage[0]); } - - Vector() {} - }; - - using vec2 = Vector; - using vec3 = Vector; - using vec4 = Vector; - - using dvec2 = Vector; - using dvec3 = Vector; - using dvec4 = Vector; - - using ivec2 = Vector; - using ivec3 = Vector; - using ivec4 = Vector; - - using uvec2 = Vector; - using uvec3 = Vector; - using uvec4 = Vector; - - // A 2D rectangle, meant to be used for stuff like scissor rects or viewport rects - // We're never supporting 3D rectangles, because rectangles were never meant to be 3D in the first place - // x, y: Coords of the top left vertex - // width, height: Dimensions of the rectangle. Initialized to 0 if not specified. - template - struct Rectangle { - T x, y, width, height; - - std::pair topLeft() const { return std::make_pair(x, y); } - std::pair topRight() const { return std::make_pair(x + width, y); } - std::pair bottomLeft() const { return std::make_pair(x, y + height); } - std::pair bottomRight() const { return std::make_pair(x + width, y + height); } - - Rectangle() : x(0), y(0), width(0), height(0) {} - Rectangle(T x, T y, T width, T height) : x(x), y(y), width(width), height(height) {} - - bool isEmpty() const { return width == 0 && height == 0; } - bool isLine() const { return (width == 0 && height != 0) || (width != 0 && height == 0); } - - void setEmpty() { x = y = width = height = 0; } - }; - - using Rect = Rectangle; - } // end namespace OpenGL diff --git a/include/renderer_gl/surfaces.hpp b/include/renderer_gl/surfaces.hpp index 9b150861..606e60d4 100644 --- a/include/renderer_gl/surfaces.hpp +++ b/include/renderer_gl/surfaces.hpp @@ -2,6 +2,7 @@ #include "PICA/regs.hpp" #include "boost/icl/interval.hpp" #include "helpers.hpp" +#include "math_util.hpp" #include "opengl.hpp" template @@ -10,7 +11,7 @@ using Interval = boost::icl::right_open_interval; struct ColourBuffer { u32 location; PICA::ColorFmt format; - OpenGL::uvec2 size; + Math::uvec2 size; bool valid; // Range of VRAM taken up by buffer @@ -90,6 +91,15 @@ struct ColourBuffer { } } + Math::Rect getSubRect(u32 inputAddress, u32 width, u32 height) { + // PICA textures have top-left origin while OpenGL has bottom-left origin. + // Flip the rectangle on the x axis to account for this. + const u32 startOffset = (inputAddress - location) / sizePerPixel(format); + const u32 x0 = (startOffset % (size.x() * 8)) / 8; + const u32 y0 = (startOffset / (size.x() * 8)) * 8; + return Math::Rect{x0, size.y() - y0, x0 + width, size.y() - height - y0}; + } + bool matches(ColourBuffer& other) { return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y(); @@ -103,7 +113,7 @@ struct ColourBuffer { struct DepthBuffer { u32 location; PICA::DepthFmt format; - OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer + Math::uvec2 size; // Implicitly set to the size of the framebuffer bool valid; // Range of VRAM taken up by buffer diff --git a/include/renderer_gl/textures.hpp b/include/renderer_gl/textures.hpp index 8667716a..c8836b5e 100644 --- a/include/renderer_gl/textures.hpp +++ b/include/renderer_gl/textures.hpp @@ -4,6 +4,7 @@ #include "PICA/regs.hpp" #include "boost/icl/interval.hpp" #include "helpers.hpp" +#include "math_util.hpp" #include "opengl.hpp" template @@ -13,7 +14,7 @@ struct Texture { u32 location; u32 config; // Magnification/minification filter, wrapping configs, etc PICA::TextureFmt format; - OpenGL::uvec2 size; + Math::uvec2 size; bool valid; // Range of VRAM taken up by buffer diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 506f7db2..2806ae48 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -536,7 +536,7 @@ void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 co OpenGL::Framebuffer RendererGL::getColourFBO() { // We construct a colour buffer object and see if our cache has any matching colour buffers in it - // If not, we allocate a texture & FBO for our framebuffer and store it in the cache + // If not, we allocate a texture & FBO for our framebuffer and store it in the cache ColourBuffer sampleBuffer(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1]); auto buffer = colourBufferCache.find(sampleBuffer); @@ -598,25 +598,34 @@ void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u const PICA::Scaling scaling = static_cast(Helpers::getBits<24, 2>(flags)); u32 outputWidth = outputSize & 0xffff; + u32 outputHeight = outputSize >> 16; + + if (inputWidth != outputWidth) { + Helpers::warn("Strided display transfer is not handled correctly!\n"); + } + + auto srcFramebuffer = getColourBuffer(inputAddr, inputFormat, inputWidth, inputHeight); + Math::Rect srcRect = srcFramebuffer.getSubRect(inputAddr, outputWidth, outputHeight); + + // Apply scaling for the destination rectangle. if (scaling == PICA::Scaling::X || scaling == PICA::Scaling::XY) { outputWidth >>= 1; } - u32 outputHeight = outputSize >> 16; if (scaling == PICA::Scaling::XY) { outputHeight >>= 1; } - // If there's a framebuffer at this address, use it. Otherwise go back to our old hack and display framebuffer 0 - // Displays are hard I really don't want to try implementing them because getting a fast solution is terrible - auto srcFramebuffer = getColourBuffer(inputAddr, inputFormat, inputWidth, inputHeight); auto dstFramebuffer = getColourBuffer(outputAddr, outputFormat, outputWidth, outputHeight); + Math::Rect dstRect = dstFramebuffer.getSubRect(outputAddr, outputWidth, outputHeight); Helpers::warn("Display transfer with outputAddr %08X\n", outputAddr); // Blit the framebuffers srcFramebuffer.fbo.bind(OpenGL::ReadFramebuffer); dstFramebuffer.fbo.bind(OpenGL::DrawFramebuffer); - glBlitFramebuffer(0, 0, inputWidth, inputHeight, 0, 0, outputWidth, outputHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(srcRect.start.x(), srcRect.start.y(), srcRect.end.x(), srcRect.end.y(), + dstRect.start.x(), dstRect.start.y(), dstRect.end.x(), dstRect.end.y(), + GL_COLOR_BUFFER_BIT, GL_LINEAR); } ColourBuffer RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height) {