Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,27 @@ namespace
{
bool writeFramebuffer;
bool preserveDestinationAlpha;
bool writeDepth;
};

AlphaTestResult classifyAlphaTest(uint64_t testReg, uint8_t alpha)
{
const bool pass = passesAlphaTest(testReg, alpha);
if (pass)
return {true, false};
return {true, false, true};

// TEST.AFAIL controls what happens when the alpha comparison fails.
switch (static_cast<uint8_t>((testReg >> 12) & 0x3u))
{
case 1: // FB_ONLY
return {true, false};
return {true, false, false};
case 2: // ZB_ONLY
return {false, false, true};
case 3: // RGB_ONLY
return {true, true};
return {true, true, false};
case 0: // KEEP
case 2: // ZB_ONLY
default:
return {false, false};
return {false, false, false};
}
}

Expand Down Expand Up @@ -417,7 +419,7 @@ void GSRasterizer::writePixel(GS *gs, int x, int y, int z, uint8_t r, uint8_t g,

const AlphaTestResult alphaTest = classifyAlphaTest(ctx.test, a);

if (!alphaTest.writeFramebuffer)
if (!alphaTest.writeFramebuffer && !alphaTest.writeDepth)
return;

u8* vram = gs->m_vram;
Expand Down Expand Up @@ -544,9 +546,12 @@ void GSRasterizer::writePixel(GS *gs, int x, int y, int z, uint8_t r, uint8_t g,
pixel = Rgba8888ToRgba5551(pixel);
}

gs->WriteVram(fpsm, fbp, fbw, x, y, pixel);
if (alphaTest.writeFramebuffer)
{
gs->WriteVram(fpsm, fbp, fbw, x, y, pixel);
}

if (!zmask)
if (alphaTest.writeDepth && !zmask)
{
gs->WriteVram(zpsm, zbp, fbw, x, y, z);
}
Expand Down
184 changes: 184 additions & 0 deletions ps2xTest/src/ps2_gs_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "ps2_runtime.h"
#include "ps2_stubs.h"
#include "ps2_syscalls.h"
#include "runtime/ps2_gs_common.h"
#include "runtime/ps2_gs_gpu.h"
#include "runtime/ps2_gs_memory.h"
#include "runtime/ps2_gs_psmct32.h"
Expand Down Expand Up @@ -3068,6 +3069,189 @@ void register_ps2_gs_tests()
"AFAIL=RGB_ONLY should update RGB while preserving destination alpha");
});

tc.Run("GS alpha test AFAIL Z-buffer-only writes depth but not color", [](TestCase &t)
{
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);
GS gs;
gs.init(vram.data(), static_cast<uint32_t>(vram.size()), nullptr);

constexpr uint32_t kFbw = 1u;
constexpr uint32_t kZbp = 1u; // ZBUF page, distinct from the color page
const uint32_t zBlock = GSInternal::framePageBaseToBlock(kZbp);

constexpr uint32_t kColorSentinel = 0xAB030201u;
constexpr uint32_t kZSentinel = 0x11111111u;
constexpr uint32_t kDrawnZ = 0x12345678u;

std::memcpy(vram.data(), &kColorSentinel, sizeof(kColorSentinel));
gs.WriteVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u, kZSentinel);

constexpr uint64_t kFrame =
(0ull << 0) |
(1ull << 16) |
(static_cast<uint64_t>(GS_PSM_CT32) << 24);
const uint64_t kZbuf = static_cast<uint64_t>(kZbp) << 0; // ZBP=1, PSM nibble=0 (Z32), ZMSK=0
constexpr uint64_t kScissor =
(0ull << 0) |
(0ull << 16) |
(0ull << 32) |
(0ull << 48);
constexpr uint64_t kTest =
1ull | // ATE
(5ull << 1) | // ATST = GEQUAL
(0x80ull << 4) | // AREF
(2ull << 12) | // AFAIL = ZB_ONLY
(1ull << 17); // ZTST = ALWAYS
constexpr uint64_t kPrim =
static_cast<uint64_t>(GS_PRIM_POINT);
constexpr uint64_t kRgbaq =
(0x12ull << 0) |
(0x34ull << 8) |
(0x56ull << 16) |
(0x00ull << 24) |
(0x3F800000ull << 32); // q = 1.0f
const uint64_t kXyz2 = static_cast<uint64_t>(kDrawnZ) << 32;

gs.writeRegister(GS_REG_FRAME_1, kFrame);
gs.writeRegister(GS_REG_ZBUF_1, kZbuf);
gs.writeRegister(GS_REG_SCISSOR_1, kScissor);
gs.writeRegister(GS_REG_TEST_1, kTest);
gs.writeRegister(GS_REG_PRIM, kPrim);
gs.writeRegister(GS_REG_RGBAQ, kRgbaq);
gs.writeRegister(GS_REG_XYZ2, kXyz2);

uint32_t pixel = 0u;
std::memcpy(&pixel, vram.data(), sizeof(pixel));
t.Equals(pixel, kColorSentinel,
"AFAIL=ZB_ONLY must not write the framebuffer when the alpha test fails");

const uint32_t depthResult = gs.ReadVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u);
t.Equals(depthResult, kDrawnZ,
"AFAIL=ZB_ONLY must still write depth when the alpha test fails");
});

tc.Run("GS alpha test AFAIL framebuffer-only leaves the depth buffer unwritten", [](TestCase &t)
{
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);
GS gs;
gs.init(vram.data(), static_cast<uint32_t>(vram.size()), nullptr);

constexpr uint32_t kFbw = 1u;
constexpr uint32_t kZbp = 1u; // ZBUF page, distinct from the color page
const uint32_t zBlock = GSInternal::framePageBaseToBlock(kZbp);

constexpr uint32_t kColorSentinel = 0xAB030201u;
constexpr uint32_t kZSentinel = 0x11111111u;
constexpr uint32_t kDrawnZ = 0x12345678u;

std::memcpy(vram.data(), &kColorSentinel, sizeof(kColorSentinel));
gs.WriteVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u, kZSentinel);

constexpr uint64_t kFrame =
(0ull << 0) |
(1ull << 16) |
(static_cast<uint64_t>(GS_PSM_CT32) << 24);
const uint64_t kZbuf = static_cast<uint64_t>(kZbp) << 0; // ZBP=1, PSM nibble=0 (Z32), ZMSK=0
constexpr uint64_t kScissor =
(0ull << 0) |
(0ull << 16) |
(0ull << 32) |
(0ull << 48);
constexpr uint64_t kTest =
1ull | // ATE
(5ull << 1) | // ATST = GEQUAL
(0x80ull << 4) | // AREF
(1ull << 12) | // AFAIL = FB_ONLY
(1ull << 17); // ZTST = ALWAYS
constexpr uint64_t kPrim =
static_cast<uint64_t>(GS_PRIM_POINT);
constexpr uint64_t kRgbaq =
(0x12ull << 0) |
(0x34ull << 8) |
(0x56ull << 16) |
(0x00ull << 24) |
(0x3F800000ull << 32); // q = 1.0f
const uint64_t kXyz2 = static_cast<uint64_t>(kDrawnZ) << 32;

gs.writeRegister(GS_REG_FRAME_1, kFrame);
gs.writeRegister(GS_REG_ZBUF_1, kZbuf);
gs.writeRegister(GS_REG_SCISSOR_1, kScissor);
gs.writeRegister(GS_REG_TEST_1, kTest);
gs.writeRegister(GS_REG_PRIM, kPrim);
gs.writeRegister(GS_REG_RGBAQ, kRgbaq);
gs.writeRegister(GS_REG_XYZ2, kXyz2);

uint32_t pixel = 0u;
std::memcpy(&pixel, vram.data(), sizeof(pixel));
t.Equals(pixel, 0x00563412u,
"AFAIL=FB_ONLY must write the framebuffer when the alpha test fails");

const uint32_t depthResult = gs.ReadVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u);
t.Equals(depthResult, kZSentinel,
"AFAIL=FB_ONLY must NOT write depth when the alpha test fails");
});

tc.Run("GS alpha test AFAIL RGB-only leaves the depth buffer unwritten", [](TestCase &t)
{
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);
GS gs;
gs.init(vram.data(), static_cast<uint32_t>(vram.size()), nullptr);

constexpr uint32_t kFbw = 1u;
constexpr uint32_t kZbp = 1u; // ZBUF page, distinct from the color page
const uint32_t zBlock = GSInternal::framePageBaseToBlock(kZbp);

constexpr uint32_t kColorSentinel = 0xAB030201u;
constexpr uint32_t kZSentinel = 0x11111111u;
constexpr uint32_t kDrawnZ = 0x12345678u;

std::memcpy(vram.data(), &kColorSentinel, sizeof(kColorSentinel));
gs.WriteVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u, kZSentinel);

constexpr uint64_t kFrame =
(0ull << 0) |
(1ull << 16) |
(static_cast<uint64_t>(GS_PSM_CT32) << 24);
const uint64_t kZbuf = static_cast<uint64_t>(kZbp) << 0; // ZBP=1, PSM nibble=0 (Z32), ZMSK=0
constexpr uint64_t kScissor =
(0ull << 0) |
(0ull << 16) |
(0ull << 32) |
(0ull << 48);
constexpr uint64_t kTest =
1ull | // ATE
(5ull << 1) | // ATST = GEQUAL
(0x80ull << 4) | // AREF
(3ull << 12) | // AFAIL = RGB_ONLY
(1ull << 17); // ZTST = ALWAYS
constexpr uint64_t kPrim =
static_cast<uint64_t>(GS_PRIM_POINT);
constexpr uint64_t kRgbaq =
(0x12ull << 0) |
(0x34ull << 8) |
(0x56ull << 16) |
(0x00ull << 24) |
(0x3F800000ull << 32); // q = 1.0f
const uint64_t kXyz2 = static_cast<uint64_t>(kDrawnZ) << 32;

gs.writeRegister(GS_REG_FRAME_1, kFrame);
gs.writeRegister(GS_REG_ZBUF_1, kZbuf);
gs.writeRegister(GS_REG_SCISSOR_1, kScissor);
gs.writeRegister(GS_REG_TEST_1, kTest);
gs.writeRegister(GS_REG_PRIM, kPrim);
gs.writeRegister(GS_REG_RGBAQ, kRgbaq);
gs.writeRegister(GS_REG_XYZ2, kXyz2);

uint32_t pixel = 0u;
std::memcpy(&pixel, vram.data(), sizeof(pixel));
t.Equals(pixel, 0xAB563412u,
"AFAIL=RGB_ONLY must write RGB and preserve destination alpha when the alpha test fails");

const uint32_t depthResult = gs.ReadVram(GS_PSM_Z32, zBlock, kFbw, 0u, 0u);
t.Equals(depthResult, kZSentinel,
"AFAIL=RGB_ONLY must NOT write depth when the alpha test fails");
});

tc.Run("GS triangle fan subpixel quad fills rows without interior holes", [](TestCase &t)
{
std::vector<uint8_t> vram(PS2_GS_VRAM_SIZE, 0u);
Expand Down