gpu: Add display transfer rectangles

This commit is contained in:
GPUCode 2023-08-03 16:35:18 +03:00
parent f75a23b5a9
commit c805504f70
5 changed files with 109 additions and 88 deletions

80
include/math_util.hpp Normal file
View file

@ -0,0 +1,80 @@
#pragma once
#include <array>
namespace Math {
// Abstraction for GLSL vectors
template <typename T, int size>
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<T, size> list) { std::copy(list.begin(), list.end(), &m_storage[0]); }
Vector() {}
};
using vec2 = Vector<float, 2>;
using vec3 = Vector<float, 3>;
using vec4 = Vector<float, 4>;
using dvec2 = Vector<double, 2>;
using dvec3 = Vector<double, 3>;
using dvec4 = Vector<double, 4>;
using ivec2 = Vector<int, 2>;
using ivec3 = Vector<int, 3>;
using ivec4 = Vector<int, 4>;
using uvec2 = Vector<unsigned int, 2>;
using uvec3 = Vector<unsigned int, 3>;
using uvec4 = Vector<unsigned int, 4>;
// 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 <typename T>
struct Rectangle {
Vector<T, 2> start;
Vector<T, 2> 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<unsigned int>;
}

View file

@ -615,83 +615,4 @@ namespace OpenGL {
glBlendFuncSeparate(fac1, fac2, fac3, fac4);
}
// Abstraction for GLSL vectors
template <typename T, int size>
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<T, size> list) { std::copy(list.begin(), list.end(), &m_storage[0]); }
Vector() {}
};
using vec2 = Vector<GLfloat, 2>;
using vec3 = Vector<GLfloat, 3>;
using vec4 = Vector<GLfloat, 4>;
using dvec2 = Vector<GLdouble, 2>;
using dvec3 = Vector<GLdouble, 3>;
using dvec4 = Vector<GLdouble, 4>;
using ivec2 = Vector<GLint, 2>;
using ivec3 = Vector<GLint, 3>;
using ivec4 = Vector<GLint, 4>;
using uvec2 = Vector<GLuint, 2>;
using uvec3 = Vector<GLuint, 3>;
using uvec4 = Vector<GLuint, 4>;
// 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 <typename T>
struct Rectangle {
T x, y, width, height;
std::pair<T, T> topLeft() const { return std::make_pair(x, y); }
std::pair<T, T> topRight() const { return std::make_pair(x + width, y); }
std::pair<T, T> bottomLeft() const { return std::make_pair(x, y + height); }
std::pair<T, T> 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<GLuint>;
} // end namespace OpenGL

View file

@ -2,6 +2,7 @@
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "math_util.hpp"
#include "opengl.hpp"
template <typename T>
@ -10,7 +11,7 @@ using Interval = boost::icl::right_open_interval<T>;
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

View file

@ -4,6 +4,7 @@
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "math_util.hpp"
#include "opengl.hpp"
template <typename T>
@ -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

View file

@ -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<PICA::Scaling>(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) {