mirror of
https://github.com/xemu-project/nxdk_pgraph_tests.git
synced 2024-11-26 19:40:45 +00:00
655 lines
29 KiB
C++
655 lines
29 KiB
C++
#include "antialiasing_tests.h"
|
|
|
|
#include <pbkit/pbkit.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "configure.h"
|
|
#include "shaders/precalculated_vertex_shader.h"
|
|
#include "test_host.h"
|
|
#include "texture_generator.h"
|
|
|
|
// From pbkit.c, DMA_A is set to channel 3 by default
|
|
// NV097_SET_CONTEXT_DMA_A == NV20_TCL_PRIMITIVE_3D_SET_OBJECT1
|
|
static constexpr uint32_t kDefaultDMAChannelA = 3;
|
|
// From pbkit.c, DMA_COLOR is set to channel 9 by default.
|
|
// NV097_SET_CONTEXT_DMA_COLOR == NV20_TCL_PRIMITIVE_3D_SET_OBJECT3
|
|
static constexpr uint32_t kDefaultDMAColorChannel = 9;
|
|
// NV097_SET_CONTEXT_DMA_ZETA is set to channel 10 by default.
|
|
static constexpr uint32_t kDefaultDMAZetaChannel = 10;
|
|
|
|
constexpr uint32_t kCheckerSize = 8;
|
|
static constexpr uint32_t kCheckerboardA = 0xFF808080;
|
|
static constexpr uint32_t kCheckerboardB = 0xFF3333C0;
|
|
|
|
static constexpr const char kAANone[] = "CreateSurfaceWithCenter1";
|
|
static constexpr const char kAA2[] = "CreateSurfaceWithCenterCorner2";
|
|
static constexpr const char kAA4[] = "CreateSurfaceWithSquareOffset4";
|
|
static constexpr const char kFBAANone[] = "FBSurfaceWithCenter1";
|
|
static constexpr const char kFBAA2[] = "FBSurfaceWithCenterCorner2";
|
|
// static constexpr const char kFBAA4[] = "FBSurfaceWithSquareOffset4";
|
|
static constexpr const char kOnOffCPUWrite[] = "AAOnThenOffCPUWrite";
|
|
static constexpr const char kModifyNonFramebufferSurface[] = "SurfaceStatesAreIndependent";
|
|
static constexpr const char kFramebufferIsIndependent[] = "FramebufferNotModifiedBySurfaceState";
|
|
static constexpr const char kCPUWriteIgnoresSurfaceConfig[] = "CPUWriteIgnoresSurfaceConfig";
|
|
static constexpr const char kGPUAAWriteAfterCPUWrite[] = "GPUAAWriteAfterCPUWrite";
|
|
static constexpr const char kNonAACPURoundTrip[] = "NonAACPURoundTrip";
|
|
#ifdef ENABLE_MULTIFRAME_CPU_BLIT_TEST
|
|
static constexpr const char kMultiframeCPUBlit[] = "__MultiframeCPUBlit";
|
|
#endif
|
|
|
|
static constexpr uint32_t kTextureSize = 128;
|
|
|
|
AntialiasingTests::AntialiasingTests(TestHost &host, std::string output_dir)
|
|
: TestSuite(host, std::move(output_dir), "Antialiasing tests") {
|
|
tests_[kAANone] = [this]() { Test(kAANone, TestHost::AA_CENTER_1); };
|
|
tests_[kAA2] = [this]() { Test(kAA2, TestHost::AA_CENTER_CORNER_2); };
|
|
tests_[kAA4] = [this]() { Test(kAA4, TestHost::AA_SQUARE_OFFSET_4); };
|
|
|
|
tests_[kFBAANone] = [this]() { TestAARenderToFramebufferSurface(kFBAANone, TestHost::AA_CENTER_1); };
|
|
tests_[kFBAA2] = [this]() { TestAARenderToFramebufferSurface(kFBAA2, TestHost::AA_CENTER_CORNER_2); };
|
|
// pbkit doesn't reserve enough framebuffer RAM for this test.
|
|
// tests_[kFBAA4] = [this]() { TestAARenderToFramebufferSurface(kFBAA4, TestHost::AA_SQUARE_OFFSET_4); };
|
|
|
|
tests_[kOnOffCPUWrite] = [this]() { TestAAOnThenOffThenCPUWrite(); };
|
|
tests_[kModifyNonFramebufferSurface] = [this]() { TestModifyNonFramebufferSurface(); };
|
|
tests_[kFramebufferIsIndependent] = [this]() { TestFramebufferIsIndependentOfSurface(); };
|
|
tests_[kCPUWriteIgnoresSurfaceConfig] = [this]() { TestCPUWriteIgnoresSurfaceConfig(); };
|
|
tests_[kGPUAAWriteAfterCPUWrite] = [this]() { TestGPUAAWriteAfterCPUWrite(); };
|
|
tests_[kNonAACPURoundTrip] = [this]() { TestNonAACPURoundTrip(); };
|
|
|
|
#ifdef ENABLE_MULTIFRAME_CPU_BLIT_TEST
|
|
tests_[kMultiframeCPUBlit] = [this]() { TestMultiframeCPUBlit(); };
|
|
#endif
|
|
};
|
|
|
|
void AntialiasingTests::Initialize() {
|
|
TestSuite::Initialize();
|
|
|
|
auto shader = std::make_shared<PrecalculatedVertexShader>();
|
|
host_.SetVertexShaderProgram(shader);
|
|
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_DEPTH_TEST_ENABLE, false);
|
|
p = pb_push1(p, NV097_SET_DEPTH_MASK, false);
|
|
p = pb_push1(p, NV097_SET_STENCIL_TEST_ENABLE, false);
|
|
p = pb_push1(p, NV097_SET_STENCIL_MASK, false);
|
|
pb_end(p);
|
|
}
|
|
|
|
void AntialiasingTests::Test(const char *name, TestHost::AntiAliasingSetting aa) {
|
|
// Point the color surface at texture memory, configure the surface with some anti-aliasing mode, clear, then point
|
|
// the surface back to the framebuffer and render the texture memory.
|
|
|
|
// Create a texture with an obvious border around it.
|
|
memset(host_.GetTextureMemory(), 0xCC, kTextureSize * kTextureSize * 4);
|
|
GenerateRGBACheckerboard(host_.GetTextureMemory(), 2, 2, kTextureSize - 4, kTextureSize - 4, kTextureSize * 4,
|
|
kCheckerboardA, kCheckerboardB, kCheckerSize);
|
|
{
|
|
// Hardware will assert with a limit error if the pitch is not sufficiently large to accommodate the anti aliasing
|
|
// mode increase. Technically this should be based off of the AA mode, but in practice it's fine to use the max
|
|
// value.
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
|
|
const auto kTextureMemory = reinterpret_cast<uint32_t>(host_.GetTextureMemoryForStage(0));
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, VRAM_ADDR(kTextureMemory));
|
|
pb_end(p);
|
|
}
|
|
|
|
// To allow the test to be run more than once, a dummy draw is done so that the next surface format call will recreate
|
|
// the xemu SurfaceBinding.
|
|
{
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_R5G6B5, TestHost::SZF_Z16, kTextureSize, kTextureSize);
|
|
NoOpDraw();
|
|
}
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0, 0, 0,
|
|
0, aa);
|
|
|
|
// A nop draw is done to finish forcing the creation of the surface.
|
|
NoOpDraw();
|
|
|
|
{
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAColorChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, 0);
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
Draw();
|
|
|
|
pb_print("%s\n", name);
|
|
pb_draw_text_screen();
|
|
host_.FinishDraw(allow_saving_, output_dir_, name);
|
|
}
|
|
|
|
void AntialiasingTests::TestAARenderToFramebufferSurface(const char *name, TestHost::AntiAliasingSetting aa) {
|
|
// Configure an antialiased surface coincident with the framebuffer and render to it, then display the contents.
|
|
|
|
uint32_t aa_multiplier;
|
|
switch (aa) {
|
|
case TestHost::AA_CENTER_1:
|
|
aa_multiplier = 1;
|
|
break;
|
|
|
|
case TestHost::AA_CENTER_CORNER_2:
|
|
aa_multiplier = 2;
|
|
break;
|
|
|
|
case TestHost::AA_SQUARE_OFFSET_4:
|
|
aa_multiplier = 4;
|
|
break;
|
|
}
|
|
|
|
{
|
|
const uint32_t pitch = host_.GetFramebufferWidth() * 4 * aa_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, pitch) | SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, pitch));
|
|
// Point zeta at an unbounded DMA channel so it won't fail limit checks, it won't be written to anyway.
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_ZETA, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_ZETA_OFFSET, VRAM_ADDR(host_.GetTextureMemory()));
|
|
pb_end(p);
|
|
}
|
|
|
|
host_.SetSurfaceFormat(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight(), false, 0, 0, 0, 0, aa);
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
{
|
|
host_.SetFinalCombiner0Just(TestHost::SRC_DIFFUSE);
|
|
host_.SetFinalCombiner1Just(TestHost::SRC_ZERO, true, true);
|
|
host_.SetTextureStageEnabled(0, false);
|
|
host_.SetShaderStageProgram(TestHost::STAGE_NONE);
|
|
|
|
static constexpr float inset = 64.0f;
|
|
const float left = inset;
|
|
const float right = host_.GetFramebufferWidthF() - inset;
|
|
const float top = inset;
|
|
const float bottom = host_.GetFramebufferHeightF() - inset;
|
|
const float mid_x = left + (right - left) * 0.5f;
|
|
const float mid_y = top + (bottom - top) * 0.5f;
|
|
|
|
host_.Begin(TestHost::PRIMITIVE_QUADS);
|
|
host_.SetDiffuse(0.25f, 0.95f, 0.75f);
|
|
host_.SetVertex(left, mid_y, 0.1f, 1.0f);
|
|
host_.SetVertex(mid_x, top, 0.1f, 1.0f);
|
|
host_.SetVertex(right, mid_y, 0.1f, 1.0f);
|
|
host_.SetVertex(mid_x, bottom, 0.1f, 1.0f);
|
|
host_.End();
|
|
}
|
|
|
|
pb_print("%s\n", name);
|
|
pb_draw_text_screen();
|
|
host_.FinishDraw(allow_saving_, output_dir_, name);
|
|
|
|
{
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_ZETA, kDefaultDMAZetaChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_ZETA_OFFSET, 0);
|
|
pb_end(p);
|
|
}
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
}
|
|
|
|
void AntialiasingTests::TestAAOnThenOffThenCPUWrite() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// Configure the framebuffer surface with some anti-aliasing mode, clear, change the mode, modify it by writing
|
|
// directly to VRAM, then render.
|
|
|
|
{
|
|
// NOTE: Hardware will assert with a limit error if the pitch is not sufficiently large to accommodate the anti
|
|
// aliasing mode increase (for both color and zeta, even if zeta is not being written to).
|
|
//
|
|
// The actual backbuffer needs to be used in order to display the test results, but pbkit does not allocate
|
|
// sufficient memory for fullscreen AA. Therefore a nop draw is performed with a reduced size in order to force
|
|
// xemu to create the surface without asserting on an oversize error.
|
|
static constexpr uint32_t kRenderSize = 256;
|
|
static constexpr uint32_t kAAMultiplier = 2;
|
|
const uint32_t kAAFramebufferPitch = kRenderSize * 4 * kAAMultiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kAAFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kAAFramebufferPitch));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kRenderSize, kRenderSize, false, 0, 0, 0,
|
|
0, TestHost::AA_CENTER_CORNER_2);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// Reset the surface to the normal backbuffer and do a CPU rendering into it.
|
|
{
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(pb_back_buffer(), 0, 0, host_.GetFramebufferWidth(), host_.GetFramebufferHeight(),
|
|
kFramebufferPitch, kCheckerboardA, 0xFF5555FF, kCheckerSize);
|
|
}
|
|
|
|
// pbkit's text drawing routines use the 3D pipeline which causes xemu to recreate the surface and masks the bug.
|
|
// pb_print("%s\n", kOnOffCPUWrite);
|
|
// pb_draw_text_screen();
|
|
host_.FinishDraw(allow_saving_, output_dir_, kOnOffCPUWrite);
|
|
}
|
|
|
|
void AntialiasingTests::TestModifyNonFramebufferSurface() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// This test verifies that setting the color target to something other than the framebuffer does not modify the
|
|
// behavior of the framebuffer.
|
|
//
|
|
// 1. Configure the framebuffer surface normally (do a nop draw to force xemu to create a surface)
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
NoOpDraw();
|
|
|
|
// 2. Configure some other surface (do a nop draw to force xemu to create a surface)
|
|
{
|
|
// Hardware will assert with a limit error if the pitch is not sufficiently large to accommodate the anti aliasing
|
|
// mode increase. Technically this should be based off of the AA mode, but in practice it's fine to use the max
|
|
// value.
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
|
|
const auto kTextureMemory = reinterpret_cast<uint32_t>(host_.GetTextureMemoryForStage(0));
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, VRAM_ADDR(kTextureMemory));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0, 0,
|
|
0, 0, TestHost::AA_CENTER_CORNER_2);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// 3. Do a CPU copy to the framebuffer
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(pb_back_buffer(), 0, 0, host_.GetFramebufferWidth(), host_.GetFramebufferHeight(),
|
|
kFramebufferPitch, 0xFF008080, kCheckerboardB, kCheckerSize);
|
|
|
|
// pbkit's text drawing routines use the 3D pipeline which causes xemu to recreate the surface and masks the bug.
|
|
// pb_print("%s\n", kOnOffCPUWrite);
|
|
// pb_draw_text_screen();
|
|
|
|
// Expected behavior is that the framebuffer looks normal, regardless of the fact that the unused texture surface is
|
|
// set up as AA.
|
|
host_.FinishDraw(allow_saving_, output_dir_, kModifyNonFramebufferSurface);
|
|
|
|
{
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAColorChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, 0);
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
}
|
|
|
|
void AntialiasingTests::TestFramebufferIsIndependentOfSurface() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// This test verifies that setting the color target for the framebuffer is irrelevant when direct CPU writes are done,
|
|
// the framebuffer configuration is entirely handled by AvSetDisplayMode.
|
|
|
|
// 1. Configure the framebuffer surface to use a non-standard pitch and size, then render to it.
|
|
{
|
|
// Hardware will assert with a limit error if the pitch is not sufficiently large to accommodate the anti aliasing
|
|
// mode increase. Technically this should be based off of the AA mode, but in practice it's fine to use the max
|
|
// value.
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0, 0,
|
|
0, 0, TestHost::AA_CENTER_CORNER_2);
|
|
|
|
host_.Begin(TestHost::PRIMITIVE_TRIANGLES);
|
|
host_.SetDiffuse(0xFFFF00FF);
|
|
host_.SetVertex(0.0f, kTextureSize / 2.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(kTextureSize, 0.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(kTextureSize, kTextureSize, 0.1f, 1.0f);
|
|
host_.End();
|
|
}
|
|
|
|
// 2. Do a CPU copy to the framebuffer
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(pb_back_buffer(), 0, 0, host_.GetFramebufferWidth(), host_.GetFramebufferHeight(),
|
|
kFramebufferPitch, 0xFF222222, 0xFF88AA00, kCheckerSize);
|
|
|
|
// Expected behavior is that the framebuffer looks normal, regardless of the fact that it was a 3d draw target with
|
|
// params that do not matc AvSetDisplayMode.
|
|
host_.FinishDraw(allow_saving_, output_dir_, kFramebufferIsIndependent);
|
|
|
|
// Restore the framebuffer surface format for future tests.
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
}
|
|
|
|
void AntialiasingTests::TestCPUWriteIgnoresSurfaceConfig() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// This test verifies that direct writes to VRAM bypass any surface configuration and must not be interpreted using
|
|
// the current xemu SurfaceBinding.
|
|
|
|
// Configure a surface pointing at texture memory and do a no-op draw to force xemu to create a SurfaceBinding.
|
|
{
|
|
// Hardware will assert with a limit error if the pitch is not sufficiently large to accommodate the anti aliasing
|
|
// mode increase. Technically this should be based off of the AA mode, but in practice it's fine to use the max
|
|
// value.
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
|
|
const auto kTextureMemory = reinterpret_cast<uint32_t>(host_.GetTextureMemoryForStage(0));
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, VRAM_ADDR(kTextureMemory));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0, 0,
|
|
0, 0, TestHost::AA_SQUARE_OFFSET_4);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// Do a CPU copy to texture memory, ignoring the AA setting.
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(host_.GetTextureMemoryForStage(0), 0, 0, kTextureSize, kTextureSize, kTextureSize * 4,
|
|
0xFFCC3333, kCheckerboardB, kCheckerSize);
|
|
|
|
// Point the output surface back at the framebuffer.
|
|
{
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAColorChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, 0);
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
|
|
// Restore the surface format to allow the texture to be rendered.
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
|
|
// Draw a quad with the texture.
|
|
Draw();
|
|
|
|
pb_print("%s\n", kCPUWriteIgnoresSurfaceConfig);
|
|
pb_draw_text_screen();
|
|
|
|
host_.FinishDraw(allow_saving_, output_dir_, kCPUWriteIgnoresSurfaceConfig);
|
|
}
|
|
|
|
void AntialiasingTests::TestGPUAAWriteAfterCPUWrite() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// This test checks the behavior of doing a GPU-based write to an anti-aliased surface that was initialized via a
|
|
// CPU-based blit.
|
|
|
|
// Configure a surface pointing at texture memory and do a no-op draw to force xemu to create a SurfaceBinding.
|
|
{
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
const auto kTextureMemory = reinterpret_cast<uint32_t>(host_.GetTextureMemoryForStage(0));
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, VRAM_ADDR(kTextureMemory));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0, 0,
|
|
0, 0, TestHost::AA_SQUARE_OFFSET_4);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// Do a CPU copy to texture memory, ignoring the AA setting.
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(host_.GetTextureMemoryForStage(0), 0, 0, kTextureSize, kTextureSize, kTextureSize * 4,
|
|
0xFF333333, 0xFFEEAA33, kCheckerSize);
|
|
|
|
// Do a GPU-based draw to the texture memory (respecting the AA setting)
|
|
{
|
|
host_.SetFinalCombiner0Just(TestHost::SRC_DIFFUSE);
|
|
host_.SetFinalCombiner1Just(TestHost::SRC_ZERO, true, true);
|
|
host_.SetTextureStageEnabled(0, false);
|
|
host_.SetShaderStageProgram(TestHost::STAGE_NONE);
|
|
|
|
host_.Begin(TestHost::PRIMITIVE_TRIANGLES);
|
|
host_.SetDiffuse(0xFFDDCC00);
|
|
host_.SetVertex(5.0f, kTextureSize / 2.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(kTextureSize - 5.0f, 5.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(kTextureSize - 5.0f, kTextureSize - 5.0f, 0.1f, 1.0f);
|
|
host_.End();
|
|
}
|
|
|
|
// Point the output surface back at the framebuffer.
|
|
{
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAColorChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, 0);
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
|
|
// Restore the surface format to allow the texture to be rendered.
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
|
|
// Draw a quad with the texture. Note that the texture configuration used in the draw routine assumes a
|
|
// non-antialiased pitch, so the GPU render should result in lines of pixels with gaps (as the AA pitch is > the
|
|
// texture pitch).
|
|
Draw();
|
|
|
|
pb_print("%s\n", kGPUAAWriteAfterCPUWrite);
|
|
pb_draw_text_screen();
|
|
|
|
host_.FinishDraw(allow_saving_, output_dir_, kGPUAAWriteAfterCPUWrite);
|
|
}
|
|
|
|
void AntialiasingTests::TestNonAACPURoundTrip() {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// This test verifies that nop draws to a CPU blitted surface are truly nops.
|
|
|
|
// Configure a surface pointing at texture memory and do a no-op draw to force xemu to create a SurfaceBinding.
|
|
// In this case, anti-aliasing is left off to verify that xemu's behavior is correct when dealing with a pitch that
|
|
// does not match the content.
|
|
{
|
|
static constexpr uint32_t anti_aliasing_multiplier = 4;
|
|
const auto kTextureMemory = reinterpret_cast<uint32_t>(host_.GetTextureMemoryForStage(0));
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAChannelA);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, VRAM_ADDR(kTextureMemory));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// Do a CPU copy to texture memory.
|
|
WaitForGPU();
|
|
GenerateRGBACheckerboard(host_.GetTextureMemoryForStage(0), 0, 0, kTextureSize, kTextureSize, kTextureSize * 4,
|
|
0xFF336666, 0xFF33FF33, kCheckerSize);
|
|
|
|
// Point the output surface back at the framebuffer.
|
|
{
|
|
const uint32_t kFramebufferPitch = host_.GetFramebufferWidth() * 4;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_CONTEXT_DMA_COLOR, kDefaultDMAColorChannel);
|
|
p = pb_push1(p, NV097_SET_SURFACE_COLOR_OFFSET, 0);
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
|
|
// Restore the surface format to allow the texture to be rendered.
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
|
|
// Draw a quad with the texture. Note that the texture configuration used in the draw routine assumes a
|
|
// non-antialiased pitch, so the GPU render should result in lines of pixels with gaps (as the AA pitch is > the
|
|
// texture pitch).
|
|
Draw();
|
|
|
|
pb_print("%s\n", kNonAACPURoundTrip);
|
|
pb_draw_text_screen();
|
|
|
|
host_.FinishDraw(allow_saving_, output_dir_, kNonAACPURoundTrip);
|
|
}
|
|
|
|
#ifdef ENABLE_MULTIFRAME_CPU_BLIT_TEST
|
|
void AntialiasingTests::TestMultiframeCPUBlit() {
|
|
static constexpr uint32_t kNumFrames = 120;
|
|
static constexpr uint32_t kColors[][2] = {
|
|
{0xFF000000, 0xFFCCCCCC}, {0xFFFF3333, 0xFF000000}, {0xFF000000, 0xFF3333FF},
|
|
{0xFF33FF33, 0xFF000000}, {0xFF000000, 0xFFCC44CC}, {0xFFCCCC44, 0xFF000000},
|
|
};
|
|
|
|
const auto width = host_.GetFramebufferWidth();
|
|
const auto height = host_.GetFramebufferHeight();
|
|
const auto kFramebufferPitch = width * 4;
|
|
|
|
for (auto i = 0; i < kNumFrames; ++i) {
|
|
host_.PrepareDraw(0xFF050505);
|
|
|
|
// Configure a surface pointing at the current framebuffer and do a no-op draw to force xemu to create a
|
|
// SurfaceBinding with a sparse pitch.
|
|
{
|
|
static constexpr uint32_t anti_aliasing_multiplier = 2;
|
|
const uint32_t kRenderBufferPitch = kTextureSize * 4 * anti_aliasing_multiplier;
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kRenderBufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kRenderBufferPitch));
|
|
pb_end(p);
|
|
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, kTextureSize, kTextureSize, false, 0,
|
|
0, 0, 0, TestHost::AA_CENTER_CORNER_2);
|
|
NoOpDraw();
|
|
}
|
|
|
|
// Do a CPU copy to texture memory, ignoring the AA setting.
|
|
WaitForGPU();
|
|
auto color = kColors[i % (sizeof(kColors) / sizeof(kColors[0]))];
|
|
GenerateRGBACheckerboard(pb_back_buffer(), 0, 0, width, height, kFramebufferPitch, color[0], color[1], 24);
|
|
|
|
host_.FinishDraw(false, output_dir_, kMultiframeCPUBlit);
|
|
}
|
|
|
|
{
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_SET_SURFACE_PITCH,
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_COLOR, kFramebufferPitch) |
|
|
SET_MASK(NV097_SET_SURFACE_PITCH_ZETA, kFramebufferPitch));
|
|
pb_end(p);
|
|
}
|
|
host_.SetSurfaceFormatImmediate(TestHost::SCF_A8R8G8B8, TestHost::SZF_Z16, host_.GetFramebufferWidth(),
|
|
host_.GetFramebufferHeight());
|
|
}
|
|
#endif // ENABLE_MULTIFRAME_CPU_BLIT_TEST
|
|
|
|
void AntialiasingTests::Draw() const {
|
|
host_.SetFinalCombiner0Just(TestHost::SRC_TEX0);
|
|
host_.SetFinalCombiner1Just(TestHost::SRC_ZERO, true, true);
|
|
host_.SetTextureStageEnabled(0, true);
|
|
host_.SetShaderStageProgram(TestHost::STAGE_2D_PROJECTIVE);
|
|
|
|
auto &texture_stage = host_.GetTextureStage(0);
|
|
texture_stage.SetFormat(GetTextureFormatInfo(NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8));
|
|
texture_stage.SetImageDimensions(kTextureSize, kTextureSize);
|
|
host_.SetupTextureStages();
|
|
|
|
static constexpr float inset = 64.0f;
|
|
const float left = inset;
|
|
const float right = host_.GetFramebufferWidthF() - inset;
|
|
const float top = inset;
|
|
const float bottom = host_.GetFramebufferHeightF() - inset;
|
|
const float mid_x = left + (right - left) * 0.5f;
|
|
const float mid_y = top + (bottom - top) * 0.5f;
|
|
|
|
host_.Begin(TestHost::PRIMITIVE_QUADS);
|
|
host_.SetTexCoord0(0.0f, 0.0f);
|
|
host_.SetVertex(left, mid_y, 0.1f, 1.0f);
|
|
|
|
host_.SetTexCoord0(kTextureSize, 0.0f);
|
|
host_.SetVertex(mid_x, top, 0.1f, 1.0f);
|
|
|
|
host_.SetTexCoord0(kTextureSize, kTextureSize);
|
|
host_.SetVertex(right, mid_y, 0.1f, 1.0f);
|
|
|
|
host_.SetTexCoord0(0.0f, kTextureSize);
|
|
host_.SetVertex(mid_x, bottom, 0.1f, 1.0f);
|
|
host_.End();
|
|
|
|
host_.SetTextureStageEnabled(0, false);
|
|
host_.SetShaderStageProgram(TestHost::STAGE_NONE);
|
|
}
|
|
|
|
void AntialiasingTests::NoOpDraw() const {
|
|
host_.Begin(TestHost::PRIMITIVE_TRIANGLES);
|
|
host_.SetVertex(0.0f, 0.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(0.0f, 0.0f, 0.1f, 1.0f);
|
|
host_.SetVertex(0.0f, 0.0f, 0.1f, 1.0f);
|
|
host_.End();
|
|
}
|
|
|
|
void AntialiasingTests::WaitForGPU() const {
|
|
auto p = pb_begin();
|
|
p = pb_push1(p, NV097_NO_OPERATION, 0);
|
|
p = pb_push1(p, NV097_WAIT_FOR_IDLE, 0);
|
|
pb_end(p);
|
|
}
|