Skip to content
Permalink
Browse files

render: optimized Rectangle rendering

  • Loading branch information
JaCzekanski committed Aug 13, 2019
1 parent bb94c81 commit 590994b8abe59a102434de9835952423f8988ec4
@@ -22,6 +22,7 @@ GPU::~GPU() { bus.unlistenAll(busToken); }
void GPU::reload() {
auto mode = config["options"]["graphics"]["rendering_mode"].get<RenderingMode>();
softwareRendering = (mode & RenderingMode::SOFTWARE) != 0;
hardwareRendering = (mode & RenderingMode::HARDWARE) != 0;
}

void GPU::reset() {
@@ -104,6 +105,58 @@ void GPU::drawPolygon(int16_t x[4], int16_t y[4], RGB c[4], TextureInfo t, bool
}
}

void GPU::drawRectangle(const Rectangle& rect) {
if (hardwareRendering) {
int x[4], y[4];
glm::ivec2 uv[4];

x[0] = rect.pos.x;
y[0] = rect.pos.y;

x[1] = rect.pos.x + rect.size.x;
y[1] = rect.pos.y;

x[2] = rect.pos.x;
y[2] = rect.pos.y + rect.size.y;

x[3] = rect.pos.x + rect.size.x;
y[3] = rect.pos.y + rect.size.y;

uv[0].x = rect.uv.x;
uv[0].y = rect.uv.y;

uv[1].x = rect.uv.x + rect.size.x;
uv[1].y = rect.uv.y;

uv[2].x = rect.uv.x;
uv[2].y = rect.uv.y + rect.size.y;

uv[3].x = rect.uv.x + rect.size.x;
uv[3].y = rect.uv.y + rect.size.y;

int flags = 0;

Vertex v[6];
for (int i : {0, 1, 2, 1, 2, 3}) {
v[i] = {Vertex::Type::Polygon,
{x[i], y[i]},
{rect.color.r, rect.color.g, rect.color.b},
{uv[i].x, uv[i].y},
rect.bits,
{rect.clut.x, rect.clut.y},
{rect.texpage.x, rect.texpage.y},
flags,
gp0_e2,
gp0_e6};
vertices.push_back(v[i]);
}
}

if (softwareRendering) {
Render::drawRectangle(this, rect);
}
}

void GPU::cmdFillRectangle(uint8_t command) {
// I'm sorry, but it appears that C++ doesn't have local functions.
struct mask {
@@ -209,6 +262,15 @@ void GPU::cmdLine(LineArgs arg) {
}

void GPU::cmdRectangle(RectangleArgs arg) {
/* Rectangle command format
arg[0] = cmd + color (0xCCBB GGRR)
arg[1] = pos (0xYYYY XXXX)
arg[2] = Palette + tex UV (0xCLUT VVUU) (*if textured)
arg[3] = size (0xHHHH WWWW) (*if variable size) */
using vec2 = glm::ivec2;
using vec3 = glm::ivec3;
using Bits = gpu::GP0_E1::TexturePageColors;

int16_t w = arg.getSize();
int16_t h = arg.getSize();

@@ -220,52 +282,42 @@ void GPU::cmdRectangle(RectangleArgs arg) {
int16_t x = extend_sign<10>(arguments[1] & 0xffff);
int16_t y = extend_sign<10>((arguments[1] & 0xffff0000) >> 16);

int16_t _x[4];
int16_t _y[4];
RGB _c[4];

_x[0] = x;
_y[0] = y;

_x[1] = x + w;
_y[1] = y;

_x[2] = x;
_y[2] = y + h;

_x[3] = x + w;
_y[3] = y + h;

_c[0].raw = arguments[0];
_c[1].raw = arguments[0];
_c[2].raw = arguments[0];
_c[3].raw = arguments[0];
TextureInfo tex;
Rectangle rect;
rect.pos = vec2(x, y);
rect.size = vec2(w, h);
rect.color = vec3((arguments[0]) & 0xff, (arguments[0] >> 8) & 0xff, (arguments[0] >> 16) & 0xff);

if (!arg.isTextureMapped)
rect.bits = 0;
else if (gp0_e1.texturePageColors == Bits::bit4)
rect.bits = 4;
else if (gp0_e1.texturePageColors == Bits::bit8)
rect.bits = 8;
else if (gp0_e1.texturePageColors == Bits::bit15)
rect.bits = 16;
rect.isSemiTransparent = arg.semiTransparency;
rect.isRawTexture = arg.isRawTexture;

if (arg.isTextureMapped) {
int texX = arguments[2] & 0xff;
int texY = (arguments[2] & 0xff00) >> 8;

tex.palette = arguments[2];
tex.texpage = (gp0_e1._reg << 16);

tex.uv[0].x = texX;
tex.uv[0].y = texY;

tex.uv[1].x = texX + w;
tex.uv[1].y = texY;

tex.uv[2].x = texX;
tex.uv[2].y = texY + h;

tex.uv[3].x = texX + w;
tex.uv[3].y = texY + h;
union Argument2 {
struct {
uint32_t u : 8;
uint32_t v : 8;
uint32_t clutX : 6;
uint32_t clutY : 9;
uint32_t : 1;
};
uint32_t _raw;
Argument2(uint32_t arg) : _raw(arg) {}
};
Argument2 p = arguments[2];

rect.uv = vec2(p.u, p.v);
rect.clut = vec2(p.clutX * 16, p.clutY);
rect.texpage = vec2(gp0_e1.texturePageBaseX * 64, gp0_e1.texturePageBaseY * 256);
}
int flags = 0;
if (arg.semiTransparency) flags |= Vertex::SemiTransparency;
if (arg.isRawTexture) flags |= Vertex::RawTexture;

drawPolygon(_x, _y, _c, tex, true, arg.isTextureMapped, flags);
drawRectangle(rect);

cmd = Command::None;
}
@@ -3,6 +3,7 @@
#include <glm/glm.hpp>
#include <vector>
#include "psx_color.h"
#include "rectangle.h"
#include "registers.h"

#define VRAM ((uint16_t(*)[VRAM_WIDTH])vram.data())
@@ -94,6 +95,7 @@ class GPU {
std::vector<Vertex> vertices;

bool softwareRendering;
bool hardwareRendering;

void reset();
void cmdFillRectangle(uint8_t command);
@@ -106,6 +108,7 @@ class GPU {
void cmdVramToVram(uint8_t command);

void drawPolygon(int16_t x[4], int16_t y[4], RGB c[4], TextureInfo t, bool isQuad = false, bool textured = false, int flags = 0);
void drawRectangle(const Rectangle& rect);

void writeGP0(uint32_t data);
void writeGP1(uint32_t data);
@@ -23,6 +23,12 @@ union PSXColor {

PSXColor() : raw(0) {}
PSXColor(uint16_t color) : raw(color) {}
PSXColor(uint8_t r, uint8_t g, uint8_t b) {
this->r = r >> 3;
this->g = g >> 3;
this->b = b >> 3;
this->k = 0;
}

PSXColor operator+(const PSXColor& rhs) {
r = std::min(r + rhs.r, 31);
@@ -0,0 +1,18 @@
#pragma once
#include <glm/glm.hpp>

struct Rectangle {
glm::ivec2 pos;
glm::ivec2 size;
glm::ivec3 color;

// Flags
int bits;
bool isSemiTransparent;
bool isRawTexture;

// Texture, valid if bits != 0
glm::ivec2 uv;
glm::ivec2 texpage; // Texture page position in VRAM (from GP0_E1)
glm::ivec2 clut; // Texture palette position in VRAM
};
@@ -5,6 +5,5 @@ class Render {
public:
static void drawLine(gpu::GPU* gpu, const int16_t x[2], const int16_t y[2], const RGB c[2]);
static void drawTriangle(gpu::GPU* gpu, gpu::Vertex v[3]);
static void drawRectangle(gpu::GPU* gpu, const int16_t x[4], const int16_t y[4], const RGB color[4], const gpu::TextureInfo tex,
bool textured, int flags);
static void drawRectangle(gpu::GPU* gpu, const Rectangle& rect);
};
@@ -37,25 +37,6 @@ bool isCw(const ivec2 v[3]) {

return n.z < 0;
}

INLINE uvec2 calculateTexel(const ivec3 s, const int area, const ivec2 tex[3], const gpu::GP0_E2 textureWindow) {
uvec2 texel( //
((int64_t)s.x * tex[0].x + (int64_t)s.y * tex[1].x + (int64_t)s.z * tex[2].x) / area, //
((int64_t)s.x * tex[0].y + (int64_t)s.y * tex[1].y + (int64_t)s.z * tex[2].y) / area //
);

// Texture is repeated outside of 256x256 window
texel.x %= 256u;
texel.y %= 256u;

// Texture masking
// texel = (texel AND(NOT(Mask * 8))) OR((Offset AND Mask) * 8)
texel.x = (texel.x & ~(textureWindow.maskX * 8)) | ((textureWindow.offsetX & textureWindow.maskX) * 8);
texel.y = (texel.y & ~(textureWindow.maskY * 8)) | ((textureWindow.offsetY & textureWindow.maskY) * 8);

return texel;
}

INLINE PSXColor doShading(const ivec3 s, const int area, const ivec2 p, const ivec3 color[3], const int flags) {
ivec3 outColor( //
(s.x * color[0].r + s.y * color[1].r + s.z * color[2].r) / area, //
@@ -65,24 +46,29 @@ INLINE PSXColor doShading(const ivec3 s, const int area, const ivec2 p, const iv

// TODO: THPS2 fading screen doesn't look as it should
if (flags & Vertex::Dithering && !(flags & Vertex::RawTexture)) {
outColor += ditherTable[p.y % 4u][p.x % 4u];
outColor += ditherTable[p.y & 3u][p.x & 3u];
outColor = glm::clamp(outColor, 0, 255);
}

return to15bit(outColor.r, outColor.g, outColor.b);
}

template <ColorDepth bits>
INLINE PSXColor fetchTex(GPU* gpu, uvec2 texel, const ivec2 texPage, const ivec2 clut) {
if constexpr (bits == ColorDepth::BIT_4) {
return tex4bit(gpu, texel, texPage, clut);
} else if constexpr (bits == ColorDepth::BIT_8) {
return tex8bit(gpu, texel, texPage, clut);
} else if constexpr (bits == ColorDepth::BIT_16) {
return tex16bit(gpu, texel, texPage);
} else {
static_assert(true, "Invalid ColorDepth parameter");
}
INLINE glm::uvec2 calculateTexel(const glm::ivec3 s, const int area, const glm::ivec2 tex[3], const gpu::GP0_E2 textureWindow) {
glm::uvec2 texel( //
((int64_t)s.x * tex[0].x + (int64_t)s.y * tex[1].x + (int64_t)s.z * tex[2].x) / area, //
((int64_t)s.x * tex[0].y + (int64_t)s.y * tex[1].y + (int64_t)s.z * tex[2].y) / area //
);

// Texture is repeated outside of 256x256 window
texel.x %= 256u;
texel.y %= 256u;

// Texture masking
// texel = (texel AND(NOT(Mask * 8))) OR((Offset AND Mask) * 8)
texel.x = (texel.x & ~(textureWindow.maskX * 8)) | ((textureWindow.offsetX & textureWindow.maskX) * 8);
texel.y = (texel.y & ~(textureWindow.maskY * 8)) | ((textureWindow.offsetY & textureWindow.maskY) * 8);

return texel;
}

template <ColorDepth bits>
@@ -124,6 +110,7 @@ INLINE void plotPixel(GPU* gpu, const ivec2 p, const ivec3 s, const int area, co

c = c * (brightness * 2.f);
}
// TODO: Textured polygons are not dithered
}

if (isSemiTransparent && (!isTextured || c.k)) {
@@ -147,6 +134,9 @@ INLINE void plotPixel(GPU* gpu, const ivec2 p, const ivec3 s, const int area, co
template <ColorDepth bits>
INLINE void triangle(GPU* gpu, const ivec2 pos[3], const ivec3 color[3], const ivec2 tex[3], const ivec2 texPage, const ivec2 clut,
const int flags, const gpu::GP0_E2 textureWindow, const gpu::GP0_E6 maskSettings) {
const int area = orient2d(pos[0], pos[1], pos[2]);
if (area == 0) return;

const ivec2 min = ivec2( //
gpu->minDrawingX(std::min({pos[0].x, pos[1].x, pos[2].x})), //
gpu->minDrawingY(std::min({pos[0].y, pos[1].y, pos[2].y})) //
@@ -167,13 +157,11 @@ INLINE void triangle(GPU* gpu, const ivec2 pos[3], const ivec3 color[3], const i
const int B20 = pos[0].x - pos[2].x;

const ivec2 minp = ivec2(min.x, min.y);

int w0_row = orient2d(pos[1], pos[2], minp);
int w1_row = orient2d(pos[2], pos[0], minp);
int w2_row = orient2d(pos[0], pos[1], minp);

const int area = orient2d(pos[0], pos[1], pos[2]);
if (area == 0) return;

ivec2 p;

for (p.y = min.y; p.y < max.y; p.y++) {
@@ -229,8 +217,12 @@ void Render::drawTriangle(GPU* gpu, Vertex v[3]) {
if (abs(pos[j].y - pos[(j + 1) % 3].y) >= 512) return;
}

if (bits == 0) triangle<ColorDepth::NONE>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
if (bits == 4) triangle<ColorDepth::BIT_4>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
if (bits == 8) triangle<ColorDepth::BIT_8>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
if (bits == 16) triangle<ColorDepth::BIT_16>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
if (bits == 0)
triangle<ColorDepth::NONE>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
else if (bits == 4)
triangle<ColorDepth::BIT_4>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
else if (bits == 8)
triangle<ColorDepth::BIT_8>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
else if (bits == 16)
triangle<ColorDepth::BIT_16>(gpu, pos, color, texcoord, texpage, clut, flags, textureWindow, maskSettings);
}

0 comments on commit 590994b

Please sign in to comment.
You can’t perform that action at this time.