Reformat all code using clang-format

This commit is contained in:
Léo Lam
2018-02-25 18:47:37 +01:00
parent 0172e0f191
commit dfba6d08e2
25 changed files with 3062 additions and 3069 deletions

View File

@@ -75,62 +75,57 @@
* correctly, e.g. printf("Value: %d", (s32)some_register.some_signed_fields);
*
*/
template<std::size_t position, std::size_t bits, typename T>
template <std::size_t position, std::size_t bits, typename T>
struct BitField
{
private:
// This constructor might be considered ambiguous:
// Would it initialize the storage or just the bitfield?
// Hence, delete it. Use the assignment operator to set bitfield values!
BitField(T val) = delete;
// This constructor might be considered ambiguous:
// Would it initialize the storage or just the bitfield?
// Hence, delete it. Use the assignment operator to set bitfield values!
BitField(T val) = delete;
public:
// Force default constructor to be created
// so that we can use this within unions
BitField() = default;
// Force default constructor to be created
// so that we can use this within unions
BitField() = default;
BitField& operator = (T val)
{
storage = (storage & ~GetMask()) | ((val<<position) & GetMask());
return *this;
}
BitField& operator=(T val)
{
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
return *this;
}
operator T() const
{
if (std::numeric_limits<T>::is_signed)
{
std::size_t shift = 8 * sizeof(T) - bits;
return (T)(((storage & GetMask()) << (shift - position)) >> shift);
}
else
{
return (T)((storage & GetMask()) >> position);
}
}
operator T() const
{
if (std::numeric_limits<T>::is_signed)
{
std::size_t shift = 8 * sizeof(T) - bits;
return (T)(((storage & GetMask()) << (shift - position)) >> shift);
}
else
{
return (T)((storage & GetMask()) >> position);
}
}
private:
// StorageType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
typedef typename std::conditional<std::is_enum<T>::value,
std::underlying_type<T>,
std::enable_if<true,T>>::type::type StorageType;
// StorageType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::enable_if<true, T>>::type::type StorageType;
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
StorageType GetMask() const
{
return ((~(StorageTypeU)0) >> (8*sizeof(T) - bits)) << position;
}
StorageType GetMask() const { return ((~(StorageTypeU)0) >> (8 * sizeof(T) - bits)) << position; }
StorageType storage;
StorageType storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
// And, you know, just in case people specify something stupid like bits=position=0x80000000
static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits");
// And, you know, just in case people specify something stupid like bits=position=0x80000000
static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits");
};

View File

@@ -5,16 +5,17 @@
#ifndef _rotl
static inline u32 _rotl(u32 x, int shift)
{
shift &= 31;
if (!shift) return x;
return (x << shift) | (x >> (32 - shift));
shift &= 31;
if (!shift)
return x;
return (x << shift) | (x >> (32 - shift));
}
static inline u32 _rotr(u32 x, int shift)
{
shift &= 31;
if (!shift) return x;
return (x >> shift) | (x << (32 - shift));
shift &= 31;
if (!shift)
return x;
return (x >> shift) | (x << (32 - shift));
}
#endif

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
// This header contains type definitions that are shared between the Dolphin core and
// other parts of the code. Any definitions that are only used by the core should be
// placed in "Common.h" instead.
@@ -15,12 +14,12 @@
#include <tchar.h>
typedef uint8_t u8;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
@@ -29,12 +28,12 @@ typedef int64_t s64;
//#ifndef GEKKO
typedef uint8_t u8;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
@@ -44,4 +43,4 @@ typedef int64_t s64;
#define TCHAR char
#define LONG int
#endif // _WIN32
#endif // _WIN32

View File

@@ -2,16 +2,17 @@
struct TestStatus
{
TestStatus(const char* file, int line) : num_passes(0), num_failures(0), num_subtests(0), file(file), line(line)
{
}
TestStatus(const char* file, int line)
: num_passes(0), num_failures(0), num_subtests(0), file(file), line(line)
{
}
long long num_passes;
long long num_failures;
long long num_subtests;
long long num_passes;
long long num_failures;
long long num_subtests;
const char* file;
int line;
const char* file;
int line;
};
static TestStatus status(NULL, 0);
@@ -22,98 +23,99 @@ int server_socket;
void network_vprintf(const char* str, va_list args)
{
char buffer[4096];
// int len = vsnprintf(buffer, 4096, str, args);
int len = vsprintf(buffer, str, args);
net_send(client_socket, buffer, len+1, 0);
char buffer[4096];
// int len = vsnprintf(buffer, 4096, str, args);
int len = vsprintf(buffer, str, args);
net_send(client_socket, buffer, len + 1, 0);
}
void network_printf(const char* str, ...)
{
va_list args;
va_start(args, str);
network_vprintf(str, args);
va_end(args);
va_list args;
va_start(args, str);
network_vprintf(str, args);
va_end(args);
}
void privStartTest(const char* file, int line)
{
status = TestStatus(file, line);
status = TestStatus(file, line);
number_of_tests++;
number_of_tests++;
}
void privDoTest(bool condition, const char* file, int line, const char* fail_msg, ...)
{
va_list arglist;
va_start(arglist, fail_msg);
va_list arglist;
va_start(arglist, fail_msg);
++status.num_subtests;
++status.num_subtests;
if (condition)
{
++status.num_passes;
}
else
{
++status.num_failures;
if (condition)
{
++status.num_passes;
}
else
{
++status.num_failures;
// TODO: vprintf forwarding doesn't seem to work?
network_printf("Subtest %lld failed in %s on line %d: ", status.num_subtests, file, line);
network_vprintf(fail_msg, arglist);
network_printf("\n");
}
va_end(arglist);
// TODO: vprintf forwarding doesn't seem to work?
network_printf("Subtest %lld failed in %s on line %d: ", status.num_subtests, file, line);
network_vprintf(fail_msg, arglist);
network_printf("\n");
}
va_end(arglist);
}
void privEndTest()
{
if (0 == status.num_failures)
{
network_printf("Test %d passed (%lld subtests)\n", number_of_tests, status.num_subtests);
}
else
{
network_printf("Test %d failed (%lld subtests, %lld failures)\n", number_of_tests, status.num_subtests, status.num_failures);
}
if (0 == status.num_failures)
{
network_printf("Test %d passed (%lld subtests)\n", number_of_tests, status.num_subtests);
}
else
{
network_printf("Test %d failed (%lld subtests, %lld failures)\n", number_of_tests,
status.num_subtests, status.num_failures);
}
}
void privSimpleTest(bool condition, const char* file, int line, const char* fail_msg, ...)
{
// TODO
// TODO
}
#define SERVER_PORT 16784
void network_init()
{
struct sockaddr_in my_name;
struct sockaddr_in my_name;
my_name.sin_family = AF_INET;
my_name.sin_port = htons(SERVER_PORT);
my_name.sin_addr.s_addr = htonl(INADDR_ANY);
my_name.sin_family = AF_INET;
my_name.sin_port = htons(SERVER_PORT);
my_name.sin_addr.s_addr = htonl(INADDR_ANY);
net_init();
net_init();
server_socket = net_socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
net_setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
server_socket = net_socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
net_setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
while(net_bind(server_socket, (struct sockaddr*)&my_name, sizeof(my_name)) < 0)
{
}
while (net_bind(server_socket, (struct sockaddr*)&my_name, sizeof(my_name)) < 0)
{
}
net_listen(server_socket, 0);
net_listen(server_socket, 0);
struct sockaddr_in client_info;
socklen_t ssize = sizeof(client_info);
client_socket = net_accept(server_socket, (struct sockaddr*)&client_info, &ssize);
struct sockaddr_in client_info;
socklen_t ssize = sizeof(client_info);
client_socket = net_accept(server_socket, (struct sockaddr*)&client_info, &ssize);
network_printf("Hello world!\n");
network_printf("Hello world!\n");
}
void network_shutdown()
{
net_close(client_socket);
net_close(server_socket);
net_close(client_socket);
net_close(server_socket);
}

View File

@@ -2,16 +2,17 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <stdio.h>
#include <stdarg.h>
#include <network.h>
#include <stdarg.h>
#include <stdio.h>
#pragma once
#define SERVER_PORT 16784
#define START_TEST() privStartTest(__FILE__, __LINE__)
#define DO_TEST(condition, fail_msg, ...) privDoTest(condition, __FILE__, __LINE__, fail_msg, ##__VA_ARGS__)
#define DO_TEST(condition, fail_msg, ...) \
privDoTest(condition, __FILE__, __LINE__, fail_msg, ##__VA_ARGS__)
#define END_TEST() privEndTest()
#define SIMPLE_TEST()
@@ -20,13 +21,13 @@ void privStartTest(const char* file, int line);
void privDoTest(bool condition, const char* file, int line, const char* fail_msg, ...);
void privEndTest();
// TODO: Not implemented, yet
//void privSimpleTest(bool condition, const char* file, int line, const char* fail_msg, ...);
// void privSimpleTest(bool condition, const char* file, int line, const char* fail_msg, ...);
void network_init();
void network_shutdown();
void network_vprintf(const char* str, va_list args);
void network_printf(const char* str, ...)
#ifndef _MSC_VER
__attribute__((__format__(printf, 1, 2)))
__attribute__((__format__(printf, 1, 2)))
#endif
;
;

View File

@@ -5,44 +5,45 @@
// Float Convert To Integer Word with round-to-Zero
static void FctiwzTest()
{
START_TEST();
u64 values[][2] = {
// input expected output
{0x0000000000000000, 0xfff8000000000000}, // +0
{0x8000000000000000, 0xfff8000100000000}, // -0 (!)
{0x0000000000000001, 0xfff8000000000000}, // smallest positive subnormal
{0x000fffffffffffff, 0xfff8000000000000}, // largest subnormal
{0x3ff0000000000000, 0xfff8000000000001}, // +1
{0xbff0000000000000, 0xfff80000ffffffff}, // -1
{0xc1e0000000000000, 0xfff8000080000000}, // -(2^31)
{0x41dfffffffc00000, 0xfff800007fffffff}, // 2^31 - 1
{0x7ff0000000000000, 0xfff800007fffffff}, // +infinity
{0xfff0000000000000, 0xfff8000080000000}, // -infinity
{0xfff8000000000000, 0xfff8000080000000}, // a QNaN
{0xfff4000000000000, 0xfff8000080000000}, // a SNaN
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u64 input = values[i][0];
u64 expected = values[i][1];
u64 result = 0;
asm("fctiwz %0, %1" : "=f" (result) : "f" (input));
DO_TEST(result == expected, "fctiwz(0x%016llx):\n"
" got 0x%016llx\n"
"expected 0x%016llx", input, result, expected);
}
END_TEST();
START_TEST();
u64 values[][2] = {
// input expected output
{0x0000000000000000, 0xfff8000000000000}, // +0
{0x8000000000000000, 0xfff8000100000000}, // -0 (!)
{0x0000000000000001, 0xfff8000000000000}, // smallest positive subnormal
{0x000fffffffffffff, 0xfff8000000000000}, // largest subnormal
{0x3ff0000000000000, 0xfff8000000000001}, // +1
{0xbff0000000000000, 0xfff80000ffffffff}, // -1
{0xc1e0000000000000, 0xfff8000080000000}, // -(2^31)
{0x41dfffffffc00000, 0xfff800007fffffff}, // 2^31 - 1
{0x7ff0000000000000, 0xfff800007fffffff}, // +infinity
{0xfff0000000000000, 0xfff8000080000000}, // -infinity
{0xfff8000000000000, 0xfff8000080000000}, // a QNaN
{0xfff4000000000000, 0xfff8000080000000}, // a SNaN
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u64 input = values[i][0];
u64 expected = values[i][1];
u64 result = 0;
asm("fctiwz %0, %1" : "=f"(result) : "f"(input));
DO_TEST(result == expected, "fctiwz(0x%016llx):\n"
" got 0x%016llx\n"
"expected 0x%016llx",
input, result, expected);
}
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
FctiwzTest();
FctiwzTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -6,47 +6,48 @@
// TODO: check ps1
static void FrspTest()
{
START_TEST();
u64 values[][3] = {
// input expected output NI RN
{0x0000000000000000, 0x0000000000000000, 0b000}, // +0
{0x8000000000000000, 0x8000000000000000, 0b000}, // -0
{0x0000000000000001, 0x0000000000000000, 0b000}, // smallest positive double subnormal
{0x000fffffffffffff, 0x0000000000000000, 0b000}, // largest double subnormal
{0x3690000000000000, 0x0000000000000000, 0b000}, // largest number rounded to zero
{0x3690000000000001, 0x36a0000000000000, 0b000}, // smallest positive single subnormal
{0x380fffffffffffff, 0x0000000000000000, 0b100}, // largest single subnormal
{0x3810000000000000, 0x3810000000000000, 0b100}, // smallest positive single normal
{0x7ff0000000000000, 0x7ff0000000000000, 0b000}, // +infinity
{0xfff0000000000000, 0xfff0000000000000, 0b000}, // -infinity
{0xfff7ffffffffffff, 0xfff7ffffe0000000, 0b000}, // a SNaN
{0xffffffffffffffff, 0xffffffffe0000000, 0b000}, // a QNaN
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
// Set FPSCR[NI] and FPSCR[RN] (and FPSCR[XE] but that's okay).
asm("mtfsf 7, %0" :: "f" (values[i][2]));
START_TEST();
u64 values[][3] = {
// input expected output NI RN
{0x0000000000000000, 0x0000000000000000, 0b000}, // +0
{0x8000000000000000, 0x8000000000000000, 0b000}, // -0
{0x0000000000000001, 0x0000000000000000, 0b000}, // smallest positive double subnormal
{0x000fffffffffffff, 0x0000000000000000, 0b000}, // largest double subnormal
{0x3690000000000000, 0x0000000000000000, 0b000}, // largest number rounded to zero
{0x3690000000000001, 0x36a0000000000000, 0b000}, // smallest positive single subnormal
{0x380fffffffffffff, 0x0000000000000000, 0b100}, // largest single subnormal
{0x3810000000000000, 0x3810000000000000, 0b100}, // smallest positive single normal
{0x7ff0000000000000, 0x7ff0000000000000, 0b000}, // +infinity
{0xfff0000000000000, 0xfff0000000000000, 0b000}, // -infinity
{0xfff7ffffffffffff, 0xfff7ffffe0000000, 0b000}, // a SNaN
{0xffffffffffffffff, 0xffffffffe0000000, 0b000}, // a QNaN
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
// Set FPSCR[NI] and FPSCR[RN] (and FPSCR[XE] but that's okay).
asm("mtfsf 7, %0" ::"f"(values[i][2]));
u64 input = values[i][0];
u64 expected = values[i][1];
u64 result = 0;
asm("frsp %0, %1" : "=f" (result) : "f" (input));
DO_TEST(result == expected, "frsp(0x%016llx, NI=%lld):\n"
" got 0x%016llx\n"
"expected 0x%016llx", input, values[i][2] >> 2, result, expected);
}
END_TEST();
u64 input = values[i][0];
u64 expected = values[i][1];
u64 result = 0;
asm("frsp %0, %1" : "=f"(result) : "f"(input));
DO_TEST(result == expected, "frsp(0x%016llx, NI=%lld):\n"
" got 0x%016llx\n"
"expected 0x%016llx",
input, values[i][2] >> 2, result, expected);
}
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
FrspTest();
FrspTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -4,478 +4,437 @@
static void lwzTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x00000000,
0x41414141,
0x11111111,
};
u32 values[] = {
0xFFFFFFFF, 0x00000000, 0x41414141, 0x11111111,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[i];
asm("lwz %0, 0(%1)" : "=r" (result) : "r" (address));
DO_TEST(result == values[i], "lwz(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[i];
asm("lwz %0, 0(%1)" : "=r"(result) : "r"(address));
DO_TEST(result == values[i], "lwz(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lwzuTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x00000000,
0x41414141,
0x11111111,
};
u32 values[] = {
0xFFFFFFFF, 0x00000000, 0x41414141, 0x11111111,
};
#define CONST_LWZU(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
asm("lwzu %0, " #offset "(%1)" : "=r" (result) , "+r" (address)); \
DO_TEST(result == values[index], "lwzu(%d):\n" \
"\tgot %d\n" \
"\texpected %d", index, result, values[index]); \
DO_TEST(address == ((u32)&values[0] + offset), "lwzu(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", index, address, ((u32)&values[0] + offset)); \
} while(0)
#define CONST_LWZU(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
asm("lwzu %0, " #offset "(%1)" : "=r"(result), "+r"(address)); \
DO_TEST(result == values[index], "lwzu(%d):\n" \
"\tgot %d\n" \
"\texpected %d", \
index, result, values[index]); \
DO_TEST(address == ((u32)&values[0] + offset), "lwzu(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", \
index, address, ((u32)&values[0] + offset)); \
} while (0)
CONST_LWZU(0, 0);
CONST_LWZU(4, 1);
CONST_LWZU(8, 2);
CONST_LWZU(12, 3);
END_TEST();
CONST_LWZU(0, 0);
CONST_LWZU(4, 1);
CONST_LWZU(8, 2);
CONST_LWZU(12, 3);
END_TEST();
}
static void lwzxTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x00000000,
0x41414141,
0x11111111,
};
u32 values[] = {
0xFFFFFFFF, 0x00000000, 0x41414141, 0x11111111,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lwzx %0, %1, %2" : "=r" (result) , "+r" (offset) : "r" (address));
DO_TEST(result == values[i], "lwzx(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lwzx %0, %1, %2" : "=r"(result), "+r"(offset) : "r"(address));
DO_TEST(result == values[i], "lwzx(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lwzuxTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x00000000,
0x41414141,
0x11111111,
};
u32 values[] = {
0xFFFFFFFF, 0x00000000, 0x41414141, 0x11111111,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lwzux %0, %1, %2" : "=r" (result) , "+r" (offset) : "r" (address));
DO_TEST(result == values[i], "lwzux(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lwzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x", i, offset, (u32)&values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lwzux %0, %1, %2" : "=r"(result), "+r"(offset) : "r"(address));
DO_TEST(result == values[i], "lwzux(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lwzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x",
i, offset, (u32)&values[i]);
}
END_TEST();
}
static void lhzTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[i];
asm("lhz %0, 0(%1)" : "=r" (result) : "r" (address));
DO_TEST(result == values[i], "lhz(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[i];
asm("lhz %0, 0(%1)" : "=r"(result) : "r"(address));
DO_TEST(result == values[i], "lhz(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lhzxTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lhzx %0, %1, %2" : "=r" (result) : "r" (address), "r" (offset));
DO_TEST(result == values[i], "lhzx(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lhzx %0, %1, %2" : "=r"(result) : "r"(address), "r"(offset));
DO_TEST(result == values[i], "lhzx(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lhzuTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
#define CONST_LHZU(offset, index) \
do \
{ \
u16 result = 0; \
u32 address = (u32)&values[0]; \
asm("lhzu %0, " #offset "(%1)" : "=r" (result) , "+r" (address)); \
DO_TEST(result == values[index], "lhzu(%d):\n" \
"\tgot %d\n" \
"\texpected %d", index, result, values[index]); \
DO_TEST(address == ((u32)&values[0] + offset), "lhzu(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", index, address, ((u32)&values[0] + offset)); \
} while(0)
#define CONST_LHZU(offset, index) \
do \
{ \
u16 result = 0; \
u32 address = (u32)&values[0]; \
asm("lhzu %0, " #offset "(%1)" : "=r"(result), "+r"(address)); \
DO_TEST(result == values[index], "lhzu(%d):\n" \
"\tgot %d\n" \
"\texpected %d", \
index, result, values[index]); \
DO_TEST(address == ((u32)&values[0] + offset), "lhzu(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", \
index, address, ((u32)&values[0] + offset)); \
} while (0)
CONST_LHZU(0, 0);
CONST_LHZU(2, 1);
CONST_LHZU(4, 2);
CONST_LHZU(6, 3);
CONST_LHZU(8, 4);
CONST_LHZU(10, 5);
END_TEST();
CONST_LHZU(0, 0);
CONST_LHZU(2, 1);
CONST_LHZU(4, 2);
CONST_LHZU(6, 3);
CONST_LHZU(8, 4);
CONST_LHZU(10, 5);
END_TEST();
}
static void lhzuxTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lhzux %0, %1, %2" : "=r" (result) , "+r" (offset) : "r" (address));
DO_TEST(result == values[i], "lhzux(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lhzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x", i, offset, (u32)&values[i]);
}
END_TEST();
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u16 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lhzux %0, %1, %2" : "=r"(result), "+r"(offset) : "r"(address));
DO_TEST(result == values[i], "lhzux(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lhzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x",
i, offset, (u32)&values[i]);
}
END_TEST();
}
static void lhaTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
#define CONST_LHA(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
u32 expected = (u32)(s32)(s16)values[index]; \
asm("lha %0, " #offset "(%1)" : "=r" (result) , "+r" (address)); \
DO_TEST(result == expected, "lha(%d):\n" \
"\tgot %d\n" \
"\texpected %d", index, result, expected); \
} while(0)
#define CONST_LHA(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
u32 expected = (u32)(s32)(s16)values[index]; \
asm("lha %0, " #offset "(%1)" : "=r"(result), "+r"(address)); \
DO_TEST(result == expected, "lha(%d):\n" \
"\tgot %d\n" \
"\texpected %d", \
index, result, expected); \
} while (0)
CONST_LHA(0, 0);
CONST_LHA(2, 1);
CONST_LHA(4, 2);
CONST_LHA(6, 3);
CONST_LHA(8, 4);
CONST_LHA(10, 5);
END_TEST();
CONST_LHA(0, 0);
CONST_LHA(2, 1);
CONST_LHA(4, 2);
CONST_LHA(6, 3);
CONST_LHA(8, 4);
CONST_LHA(10, 5);
END_TEST();
}
static void lhaxTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
u32 expected = (u32)(s32)(s16)values[i];
asm("lhax %0, %1, %2" : "=r" (result) : "r" (address), "r" (offset));
DO_TEST(result == expected, "lhax(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, expected);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
u32 expected = (u32)(s32)(s16)values[i];
asm("lhax %0, %1, %2" : "=r"(result) : "r"(address), "r"(offset));
DO_TEST(result == expected, "lhax(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, expected);
}
END_TEST();
}
static void lhauTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
#define CONST_LHAU(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
u32 expected = (u32)(s32)(s16)values[index]; \
asm("lhau %0, " #offset "(%1)" : "=r" (result) , "+r" (address)); \
DO_TEST(result == expected, "lhau(%d):\n" \
"\tgot %d\n" \
"\texpected %d", index, result, expected); \
DO_TEST(address == ((u32)&values[0] + offset), "lhau(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", index, address, ((u32)&values[0] + offset)); \
} while(0)
#define CONST_LHAU(offset, index) \
do \
{ \
u32 result = 0; \
u32 address = (u32)&values[0]; \
u32 expected = (u32)(s32)(s16)values[index]; \
asm("lhau %0, " #offset "(%1)" : "=r"(result), "+r"(address)); \
DO_TEST(result == expected, "lhau(%d):\n" \
"\tgot %d\n" \
"\texpected %d", \
index, result, expected); \
DO_TEST(address == ((u32)&values[0] + offset), "lhau(%d):\n" \
"\tgot 0x%08x\n" \
"\texpected 0x%08x", \
index, address, ((u32)&values[0] + offset)); \
} while (0)
CONST_LHAU(0, 0);
CONST_LHAU(2, 1);
CONST_LHAU(4, 2);
CONST_LHAU(6, 3);
CONST_LHAU(8, 4);
CONST_LHAU(10, 5);
END_TEST();
CONST_LHAU(0, 0);
CONST_LHAU(2, 1);
CONST_LHAU(4, 2);
CONST_LHAU(6, 3);
CONST_LHAU(8, 4);
CONST_LHAU(10, 5);
END_TEST();
}
static void lhauxTest()
{
START_TEST();
START_TEST();
u16 values[] =
{
0xFFFF,
0xF000,
0x0000,
0x0F0F,
0xF0F0,
0x8888,
};
u16 values[] = {
0xFFFF, 0xF000, 0x0000, 0x0F0F, 0xF0F0, 0x8888,
};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
u32 expected = (u32)(s32)(s16)values[i];
asm("lhaux %0, %1, %2" : "=r" (result) , "+r" (address) : "r" (offset));
DO_TEST(result == expected, "lhaux(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, expected);
DO_TEST(address == ((u32)&values[0] + offset), "lhaux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x", i, offset, ((u32)&values[0] + offset));
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u32 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
u32 expected = (u32)(s32)(s16)values[i];
asm("lhaux %0, %1, %2" : "=r"(result), "+r"(address) : "r"(offset));
DO_TEST(result == expected, "lhaux(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, expected);
DO_TEST(address == ((u32)&values[0] + offset), "lhaux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x",
i, offset, ((u32)&values[0] + offset));
}
END_TEST();
}
static void lbzTest()
{
START_TEST();
START_TEST();
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[i];
asm("lbz %0, 0(%1)" : "=r" (result) : "r" (address));
DO_TEST(result == values[i], "lbz(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[i];
asm("lbz %0, 0(%1)" : "=r"(result) : "r"(address));
DO_TEST(result == values[i], "lbz(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lbzxTest()
{
START_TEST();
START_TEST();
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lbzx %0, %1, %2" : "=r" (result) : "r" (address), "r" (offset));
DO_TEST(result == values[i], "lbzx(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lbzx %0, %1, %2" : "=r"(result) : "r"(address), "r"(offset));
DO_TEST(result == values[i], "lbzx(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
}
END_TEST();
}
static void lbzuTest()
{
START_TEST();
START_TEST();
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[i];
asm("lbzu %0, 0(%1)" : "=r" (result) , "+r" (address));
DO_TEST(result == values[i], "lbzu(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
DO_TEST(address == (u32)&values[i], "lbzu(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x", i, address, (u32)&values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[i];
asm("lbzu %0, 0(%1)" : "=r"(result), "+r"(address));
DO_TEST(result == values[i], "lbzu(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
DO_TEST(address == (u32)&values[i], "lbzu(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x",
i, address, (u32)&values[i]);
}
END_TEST();
}
static void lbzuxTest()
{
START_TEST();
START_TEST();
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
u8 values[256];
for (int i = 0; i < 256; ++i)
values[i] = i;
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lbzux %0, %1, %2" : "=r" (result) , "+r" (offset) : "r" (address));
DO_TEST(result == values[i], "lbzux(%d):\n"
"\tgot %d\n"
"\texpected %d", i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lbzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x", i, offset, (u32)&values[i]);
}
END_TEST();
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++)
{
u8 result = 0;
u32 address = (u32)&values[0];
u32 offset = (u32)&values[i] - address;
asm("lbzux %0, %1, %2" : "=r"(result), "+r"(offset) : "r"(address));
DO_TEST(result == values[i], "lbzux(%d):\n"
"\tgot %d\n"
"\texpected %d",
i, result, values[i]);
DO_TEST(offset == (u32)&values[i], "lbzux(%d):\n"
"\tgot 0x%08x\n"
"\texpected 0x%08x",
i, offset, (u32)&values[i]);
}
END_TEST();
}
int main()
{
network_init();
network_init();
lwzTest();
lwzuTest();
lwzxTest();
lwzuxTest();
lwzTest();
lwzuTest();
lwzxTest();
lwzuxTest();
lhzTest();
lhzxTest();
lhzuTest();
lhzuxTest();
lhaTest();
lhaxTest();
lhauTest();
lhauxTest();
lhzTest();
lhzxTest();
lhzuTest();
lhzuxTest();
lhaTest();
lhaxTest();
lhauTest();
lhauxTest();
lbzTest();
lbzxTest();
lbzuTest();
lbzuxTest();
lbzTest();
lbzxTest();
lbzuTest();
lbzuxTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -7,49 +7,45 @@
// Some games do this (e.g. Dirt 2) and rely on correct emulation behavior.
static void GQRUnusedBitsTest()
{
START_TEST();
uint32_t output;
// based on hwtest on Wii
uint32_t expected = 0xFFFFFFFF;
asm(
"li %0, -1;"
"mtspr 914, %0;"
"mfspr %0, 914;"
: "=r"(output)
);
START_TEST();
uint32_t output;
// based on hwtest on Wii
uint32_t expected = 0xFFFFFFFF;
asm("li %0, -1;"
"mtspr 914, %0;"
"mfspr %0, 914;"
: "=r"(output));
DO_TEST(output == expected, "got %x, expected %x", output, expected);
END_TEST();
DO_TEST(output == expected, "got %x, expected %x", output, expected);
END_TEST();
}
// Check what the Broadway does if we set reserved bits in the XER (fixed-point exception) register.
static void XERUnusedBitsTest()
{
START_TEST();
uint32_t output;
// based on hwtest on Wii
uint32_t expected = 0xE000FF7F;
asm(
"li %0, -1;"
"mtspr 1, %0;"
"mfspr %0, 1;"
: "=r"(output)
);
START_TEST();
uint32_t output;
// based on hwtest on Wii
uint32_t expected = 0xE000FF7F;
asm("li %0, -1;"
"mtspr 1, %0;"
"mfspr %0, 1;"
: "=r"(output));
DO_TEST(output == expected, "got %x, expected %x", output, expected);
END_TEST();
DO_TEST(output == expected, "got %x, expected %x", output, expected);
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GQRUnusedBitsTest();
XERUnusedBitsTest();
GQRUnusedBitsTest();
XERUnusedBitsTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -14,195 +14,183 @@
static double fres_expected(double val)
{
static const int estimate_base[] = {
0x7ff800, 0x783800, 0x70ea00, 0x6a0800,
0x638800, 0x5d6200, 0x579000, 0x520800,
0x4cc800, 0x47ca00, 0x430800, 0x3e8000,
0x3a2c00, 0x360800, 0x321400, 0x2e4a00,
0x2aa800, 0x272c00, 0x23d600, 0x209e00,
0x1d8800, 0x1a9000, 0x17ae00, 0x14f800,
0x124400, 0x0fbe00, 0x0d3800, 0x0ade00,
0x088400, 0x065000, 0x041c00, 0x020c00,
};
static const int estimate_dec[] = {
0x3e1, 0x3a7, 0x371, 0x340,
0x313, 0x2ea, 0x2c4, 0x2a0,
0x27f, 0x261, 0x245, 0x22a,
0x212, 0x1fb, 0x1e5, 0x1d1,
0x1be, 0x1ac, 0x19b, 0x18b,
0x17c, 0x16e, 0x15b, 0x15b,
0x143, 0x143, 0x12d, 0x12d,
0x11a, 0x11a, 0x108, 0x106,
};
static const int estimate_base[] = {
0x7ff800, 0x783800, 0x70ea00, 0x6a0800, 0x638800, 0x5d6200, 0x579000, 0x520800,
0x4cc800, 0x47ca00, 0x430800, 0x3e8000, 0x3a2c00, 0x360800, 0x321400, 0x2e4a00,
0x2aa800, 0x272c00, 0x23d600, 0x209e00, 0x1d8800, 0x1a9000, 0x17ae00, 0x14f800,
0x124400, 0x0fbe00, 0x0d3800, 0x0ade00, 0x088400, 0x065000, 0x041c00, 0x020c00,
};
static const int estimate_dec[] = {
0x3e1, 0x3a7, 0x371, 0x340, 0x313, 0x2ea, 0x2c4, 0x2a0, 0x27f, 0x261, 0x245,
0x22a, 0x212, 0x1fb, 0x1e5, 0x1d1, 0x1be, 0x1ac, 0x19b, 0x18b, 0x17c, 0x16e,
0x15b, 0x15b, 0x143, 0x143, 0x12d, 0x12d, 0x11a, 0x11a, 0x108, 0x106,
};
union
{
double valf;
s64 vali;
};
valf = val;
s64 mantissa = vali & ((1LL << 52) - 1);
s64 sign = vali & (1ULL << 63);
s64 exponent = vali & (0x7FFLL << 52);
union
{
double valf;
s64 vali;
};
valf = val;
s64 mantissa = vali & ((1LL << 52) - 1);
s64 sign = vali & (1ULL << 63);
s64 exponent = vali & (0x7FFLL << 52);
// Special case 0
if (mantissa == 0 && exponent == 0)
return sign ? -std::numeric_limits<double>::infinity() :
std::numeric_limits<double>::infinity();
// Special case NaN-ish numbers
if (exponent == (0x7FFLL << 52))
{
if (mantissa == 0)
return sign ? -0.0 : 0.0;
return 0.0 + valf;
}
// Special case small inputs
if (exponent < (895LL << 52))
return sign ? -FLT_MAX : FLT_MAX;
// Special case large inputs
if (exponent >= (1149LL << 52))
return sign ? -0.0f : 0.0f;
// Special case 0
if (mantissa == 0 && exponent == 0)
return sign ? -std::numeric_limits<double>::infinity() :
std::numeric_limits<double>::infinity();
// Special case NaN-ish numbers
if (exponent == (0x7FFLL << 52))
{
if (mantissa == 0)
return sign ? -0.0 : 0.0;
return 0.0 + valf;
}
// Special case small inputs
if (exponent < (895LL << 52))
return sign ? -FLT_MAX : FLT_MAX;
// Special case large inputs
if (exponent >= (1149LL << 52))
return sign ? -0.0f : 0.0f;
exponent = (0x7FDLL << 52) - exponent;
exponent = (0x7FDLL << 52) - exponent;
int i = (int)(mantissa >> 37);
vali = sign | exponent;
vali |= (s64)(estimate_base[i / 1024] - (estimate_dec[i / 1024] * (i % 1024) + 1) / 2) << 29;
return valf;
int i = (int)(mantissa >> 37);
vali = sign | exponent;
vali |= (s64)(estimate_base[i / 1024] - (estimate_dec[i / 1024] * (i % 1024) + 1) / 2) << 29;
return valf;
}
static double frsqrte_expected(double val)
{
static const int estimate_base[] = {
0x3ffa000, 0x3c29000, 0x38aa000, 0x3572000,
0x3279000, 0x2fb7000, 0x2d26000, 0x2ac0000,
0x2881000, 0x2665000, 0x2468000, 0x2287000,
0x20c1000, 0x1f12000, 0x1d79000, 0x1bf4000,
0x1a7e800, 0x17cb800, 0x1552800, 0x130c000,
0x10f2000, 0x0eff000, 0x0d2e000, 0x0b7c000,
0x09e5000, 0x0867000, 0x06ff000, 0x05ab800,
0x046a000, 0x0339800, 0x0218800, 0x0105800,
};
static const int estimate_dec[] = {
0x7a4, 0x700, 0x670, 0x5f2,
0x584, 0x524, 0x4cc, 0x47e,
0x43a, 0x3fa, 0x3c2, 0x38e,
0x35e, 0x332, 0x30a, 0x2e6,
0x568, 0x4f3, 0x48d, 0x435,
0x3e7, 0x3a2, 0x365, 0x32e,
0x2fc, 0x2d0, 0x2a8, 0x283,
0x261, 0x243, 0x226, 0x20b,
};
static const int estimate_base[] = {
0x3ffa000, 0x3c29000, 0x38aa000, 0x3572000, 0x3279000, 0x2fb7000, 0x2d26000, 0x2ac0000,
0x2881000, 0x2665000, 0x2468000, 0x2287000, 0x20c1000, 0x1f12000, 0x1d79000, 0x1bf4000,
0x1a7e800, 0x17cb800, 0x1552800, 0x130c000, 0x10f2000, 0x0eff000, 0x0d2e000, 0x0b7c000,
0x09e5000, 0x0867000, 0x06ff000, 0x05ab800, 0x046a000, 0x0339800, 0x0218800, 0x0105800,
};
static const int estimate_dec[] = {
0x7a4, 0x700, 0x670, 0x5f2, 0x584, 0x524, 0x4cc, 0x47e, 0x43a, 0x3fa, 0x3c2,
0x38e, 0x35e, 0x332, 0x30a, 0x2e6, 0x568, 0x4f3, 0x48d, 0x435, 0x3e7, 0x3a2,
0x365, 0x32e, 0x2fc, 0x2d0, 0x2a8, 0x283, 0x261, 0x243, 0x226, 0x20b,
};
union
{
double valf;
s64 vali;
};
valf = val;
s64 mantissa = vali & ((1LL << 52) - 1);
s64 sign = vali & (1ULL << 63);
s64 exponent = vali & (0x7FFLL << 52);
union
{
double valf;
s64 vali;
};
valf = val;
s64 mantissa = vali & ((1LL << 52) - 1);
s64 sign = vali & (1ULL << 63);
s64 exponent = vali & (0x7FFLL << 52);
// Special case 0
if (mantissa == 0 && exponent == 0)
return sign ? -std::numeric_limits<double>::infinity() :
std::numeric_limits<double>::infinity();
// Special case NaN-ish numbers
if (exponent == (0x7FFLL << 52))
{
if (mantissa == 0)
{
if (sign)
return std::numeric_limits<double>::quiet_NaN();
return 0.0;
}
return 0.0 + valf;
}
// Negative numbers return NaN
if (sign)
return std::numeric_limits<double>::quiet_NaN();
// Special case 0
if (mantissa == 0 && exponent == 0)
return sign ? -std::numeric_limits<double>::infinity() :
std::numeric_limits<double>::infinity();
// Special case NaN-ish numbers
if (exponent == (0x7FFLL << 52))
{
if (mantissa == 0)
{
if (sign)
return std::numeric_limits<double>::quiet_NaN();
return 0.0;
}
return 0.0 + valf;
}
// Negative numbers return NaN
if (sign)
return std::numeric_limits<double>::quiet_NaN();
if (!exponent)
{
// "Normalize" denormal values
do
{
exponent -= 1LL << 52;
mantissa <<= 1;
} while (!(mantissa & (1LL << 52)));
mantissa &= (1LL << 52) - 1;
exponent += 1LL << 52;
}
if (!exponent)
{
// "Normalize" denormal values
do
{
exponent -= 1LL << 52;
mantissa <<= 1;
} while (!(mantissa & (1LL << 52)));
mantissa &= (1LL << 52) - 1;
exponent += 1LL << 52;
}
bool odd_exponent = !(exponent & (1LL << 52));
exponent = ((0x3FFLL << 52) - ((exponent - (0x3FELL << 52)) / 2)) & (0x7FFLL << 52);
bool odd_exponent = !(exponent & (1LL << 52));
exponent = ((0x3FFLL << 52) - ((exponent - (0x3FELL << 52)) / 2)) & (0x7FFLL << 52);
int i = (int)(mantissa >> 37);
vali = sign | exponent;
int index = i / 2048 + (odd_exponent ? 16 : 0);
vali |= (s64)(estimate_base[index] - estimate_dec[index] * (i % 2048)) << 26;
return valf;
int i = (int)(mantissa >> 37);
vali = sign | exponent;
int index = i / 2048 + (odd_exponent ? 16 : 0);
vali |= (s64)(estimate_base[index] - estimate_dec[index] * (i % 2048)) << 26;
return valf;
}
static inline double
fres_intrinsic(double val)
static inline double fres_intrinsic(double val)
{
double estimate;
__asm__("fres %0,%1"
/* outputs: */ : "=f" (estimate)
/* inputs: */ : "f" (val));
return estimate;
double estimate;
__asm__("fres %0,%1"
/* outputs: */
: "=f"(estimate)
/* inputs: */
: "f"(val));
return estimate;
}
static void ReciprocalTest()
{
START_TEST();
START_TEST();
for (unsigned long long i = 0; i < 0x100000000LL; i += 1)
{
union {
long long testi;
double testf;
};
union {
double expectedf;
long long expectedi;
};
testi = i << 32;
expectedf = frsqrte_expected(testf);
testf = __frsqrte(testf);
DO_TEST(testi == expectedi, "Bad frsqrte %lld %.10f %llx %.10f %llx", i, testf, testi, expectedf, expectedi);
if (testi != expectedi) break;
for (unsigned long long i = 0; i < 0x100000000LL; i += 1)
{
union
{
long long testi;
double testf;
};
union
{
double expectedf;
long long expectedi;
};
testi = i << 32;
expectedf = frsqrte_expected(testf);
testf = __frsqrte(testf);
DO_TEST(testi == expectedi, "Bad frsqrte %lld %.10f %llx %.10f %llx", i, testf, testi,
expectedf, expectedi);
if (testi != expectedi)
break;
testi = i << 32;
expectedf = fres_expected(testf);
testf = fres_intrinsic(testf);
DO_TEST(testi == expectedi, "Bad fres %lld %.10f %llx %.10f %llx", i, testf, testi, expectedf, expectedi);
if (testi != expectedi) break;
testi = i << 32;
expectedf = fres_expected(testf);
testf = fres_intrinsic(testf);
DO_TEST(testi == expectedi, "Bad fres %lld %.10f %llx %.10f %llx", i, testf, testi, expectedf,
expectedi);
if (testi != expectedi)
break;
if (!(i & ((1 << 22) - 1)))
{
network_printf("Progress %lld\n", i);
WPAD_ScanPads();
if (!(i & ((1 << 22) - 1)))
{
network_printf("Progress %lld\n", i);
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
break;
}
}
END_TEST();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
break;
}
}
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
ReciprocalTest();
ReciprocalTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -9,208 +9,194 @@
// Copied from Dolphin's Interpreter integer operations.
static u32 GetHelperMask(int mb, int me)
{
// first make 001111111111111 part
// then make 000000000001111 part, which is used to flip the bits of the first one
// do the bitflip
// and invert if backwards
u32 begin = 0xFFFFFFFF >> mb;
u32 end = me < 31 ? (0xFFFFFFFF >> (me + 1)) : 0;
u32 mask = begin ^ end;
if (me < mb)
return ~mask;
else
return mask;
// first make 001111111111111 part
// then make 000000000001111 part, which is used to flip the bits of the first one
// do the bitflip
// and invert if backwards
u32 begin = 0xFFFFFFFF >> mb;
u32 end = me < 31 ? (0xFFFFFFFF >> (me + 1)) : 0;
u32 mask = begin ^ end;
if (me < mb)
return ~mask;
else
return mask;
}
static void rlwimixTest()
{
START_TEST();
START_TEST();
u32 values[][2] =
{
{0xFFFFFFFF, 0x41414141},
{0x44444444, 0xFFFFFFFF},
{0x41414141, 0x22222222},
};
u32 values[][2] = {
{0xFFFFFFFF, 0x41414141}, {0x44444444, 0xFFFFFFFF}, {0x41414141, 0x22222222},
};
#define RLWIMIX_TEST(sh, mb, me) \
do { \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i)\
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = (values[i][0] & ~mask) | (_rotl(values[i][1], sh) & mask); \
\
u32 valueA = values[i][0]; \
u32 valueS = values[i][1]; \
\
asm ("rlwimi %0, %1, " #sh ", " #mb ", " #me \
: "+r" (valueA) \
: "r" (valueS)); \
DO_TEST(valueA == computed_result, "rlwimi %08x, %08x, " #sh ", " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", values[i][0], values[i][1], valueA, computed_result); \
} \
} while(0)
#define RLWIMIX_TEST(sh, mb, me) \
do \
{ \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i) \
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = (values[i][0] & ~mask) | (_rotl(values[i][1], sh) & mask); \
\
u32 valueA = values[i][0]; \
u32 valueS = values[i][1]; \
\
asm("rlwimi %0, %1, " #sh ", " #mb ", " #me : "+r"(valueA) : "r"(valueS)); \
DO_TEST(valueA == computed_result, "rlwimi %08x, %08x, " #sh ", " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", \
values[i][0], values[i][1], valueA, computed_result); \
} \
} while (0)
RLWIMIX_TEST(0, 0, 0);
RLWIMIX_TEST(0, 0, 15);
RLWIMIX_TEST(0, 0, 31);
RLWIMIX_TEST(0, 15, 0);
RLWIMIX_TEST(0, 15, 15);
RLWIMIX_TEST(0, 15, 31);
RLWIMIX_TEST(0, 31, 0);
RLWIMIX_TEST(15, 0, 0);
RLWIMIX_TEST(15, 0, 15);
RLWIMIX_TEST(15, 0, 31);
RLWIMIX_TEST(15, 15, 0);
RLWIMIX_TEST(15, 15, 15);
RLWIMIX_TEST(15, 15, 31);
RLWIMIX_TEST(15, 31, 0);
RLWIMIX_TEST(15, 31, 15);
RLWIMIX_TEST(15, 31, 31);
RLWIMIX_TEST(31, 0, 0);
RLWIMIX_TEST(31, 0, 15);
RLWIMIX_TEST(31, 0, 31);
RLWIMIX_TEST(31, 15, 0);
RLWIMIX_TEST(31, 15, 15);
RLWIMIX_TEST(31, 15, 31);
RLWIMIX_TEST(31, 31, 0);
RLWIMIX_TEST(31, 31, 15);
RLWIMIX_TEST(31, 31, 31);
END_TEST();
RLWIMIX_TEST(0, 0, 0);
RLWIMIX_TEST(0, 0, 15);
RLWIMIX_TEST(0, 0, 31);
RLWIMIX_TEST(0, 15, 0);
RLWIMIX_TEST(0, 15, 15);
RLWIMIX_TEST(0, 15, 31);
RLWIMIX_TEST(0, 31, 0);
RLWIMIX_TEST(15, 0, 0);
RLWIMIX_TEST(15, 0, 15);
RLWIMIX_TEST(15, 0, 31);
RLWIMIX_TEST(15, 15, 0);
RLWIMIX_TEST(15, 15, 15);
RLWIMIX_TEST(15, 15, 31);
RLWIMIX_TEST(15, 31, 0);
RLWIMIX_TEST(15, 31, 15);
RLWIMIX_TEST(15, 31, 31);
RLWIMIX_TEST(31, 0, 0);
RLWIMIX_TEST(31, 0, 15);
RLWIMIX_TEST(31, 0, 31);
RLWIMIX_TEST(31, 15, 0);
RLWIMIX_TEST(31, 15, 15);
RLWIMIX_TEST(31, 15, 31);
RLWIMIX_TEST(31, 31, 0);
RLWIMIX_TEST(31, 31, 15);
RLWIMIX_TEST(31, 31, 31);
END_TEST();
}
static void rlwinmxTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x44444444,
0x41414141,
0x12345678,
0x90ABCDEF,
};
u32 values[] = {
0xFFFFFFFF, 0x44444444, 0x41414141, 0x12345678, 0x90ABCDEF,
};
#define RLWINMX_TEST(sh, mb, me) \
do { \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i)\
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = _rotl(values[i], sh) & mask; \
\
u32 result = 0; \
u32 valueS = values[i]; \
\
asm ("rlwinm %0, %1, " #sh ", " #mb ", " #me \
: "=r" (result) \
: "r" (valueS)); \
DO_TEST(result == computed_result, "rlwinm %08x, " #sh ", " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", values[i], result, computed_result); \
} \
} while(0)
#define RLWINMX_TEST(sh, mb, me) \
do \
{ \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i) \
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = _rotl(values[i], sh) & mask; \
\
u32 result = 0; \
u32 valueS = values[i]; \
\
asm("rlwinm %0, %1, " #sh ", " #mb ", " #me : "=r"(result) : "r"(valueS)); \
DO_TEST(result == computed_result, "rlwinm %08x, " #sh ", " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", \
values[i], result, computed_result); \
} \
} while (0)
RLWINMX_TEST(0, 0, 0);
RLWINMX_TEST(0, 0, 15);
RLWINMX_TEST(0, 0, 31);
RLWINMX_TEST(0, 15, 0);
RLWINMX_TEST(0, 15, 15);
RLWINMX_TEST(0, 15, 31);
RLWINMX_TEST(0, 31, 0);
RLWINMX_TEST(15, 0, 0);
RLWINMX_TEST(15, 0, 15);
RLWINMX_TEST(15, 0, 31);
RLWINMX_TEST(15, 15, 0);
RLWINMX_TEST(15, 15, 15);
RLWINMX_TEST(15, 15, 31);
RLWINMX_TEST(15, 31, 0);
RLWINMX_TEST(15, 31, 15);
RLWINMX_TEST(15, 31, 31);
RLWINMX_TEST(31, 0, 0);
RLWINMX_TEST(31, 0, 15);
RLWINMX_TEST(31, 0, 31);
RLWINMX_TEST(31, 15, 0);
RLWINMX_TEST(31, 15, 15);
RLWINMX_TEST(31, 15, 31);
RLWINMX_TEST(31, 31, 0);
RLWINMX_TEST(31, 31, 15);
RLWINMX_TEST(31, 31, 31);
END_TEST();
RLWINMX_TEST(0, 0, 0);
RLWINMX_TEST(0, 0, 15);
RLWINMX_TEST(0, 0, 31);
RLWINMX_TEST(0, 15, 0);
RLWINMX_TEST(0, 15, 15);
RLWINMX_TEST(0, 15, 31);
RLWINMX_TEST(0, 31, 0);
RLWINMX_TEST(15, 0, 0);
RLWINMX_TEST(15, 0, 15);
RLWINMX_TEST(15, 0, 31);
RLWINMX_TEST(15, 15, 0);
RLWINMX_TEST(15, 15, 15);
RLWINMX_TEST(15, 15, 31);
RLWINMX_TEST(15, 31, 0);
RLWINMX_TEST(15, 31, 15);
RLWINMX_TEST(15, 31, 31);
RLWINMX_TEST(31, 0, 0);
RLWINMX_TEST(31, 0, 15);
RLWINMX_TEST(31, 0, 31);
RLWINMX_TEST(31, 15, 0);
RLWINMX_TEST(31, 15, 15);
RLWINMX_TEST(31, 15, 31);
RLWINMX_TEST(31, 31, 0);
RLWINMX_TEST(31, 31, 15);
RLWINMX_TEST(31, 31, 31);
END_TEST();
}
static void rlwnmxTest()
{
START_TEST();
START_TEST();
u32 values[] =
{
0xFFFFFFFF,
0x44444444,
0x41414141,
0x12345678,
0x90ABCDEF,
};
u32 values[] = {
0xFFFFFFFF, 0x44444444, 0x41414141, 0x12345678, 0x90ABCDEF,
};
#define RLWNMX_TEST(mb, me) \
do { \
for (int sh = 0; sh < 32; ++sh) \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i)\
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = _rotl(values[i], sh & 0x1F) & mask; \
\
u32 result = 0; \
u32 valueS = values[i]; \
\
asm ("rlwnm %0, %1, %2, " #mb ", " #me \
: "=r" (result) \
: "r" (valueS), \
"r" (sh)); \
DO_TEST(result == computed_result, "rlwnm %08x, %d, " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", values[i], sh, result, computed_result); \
} \
} while(0)
#define RLWNMX_TEST(mb, me) \
do \
{ \
for (int sh = 0; sh < 32; ++sh) \
for (int i = 0; i < (sizeof(values) / sizeof(values[0])); ++i) \
{ \
u32 mask = GetHelperMask(mb, me); \
u32 computed_result = _rotl(values[i], sh & 0x1F) & mask; \
\
u32 result = 0; \
u32 valueS = values[i]; \
\
asm("rlwnm %0, %1, %2, " #mb ", " #me : "=r"(result) : "r"(valueS), "r"(sh)); \
DO_TEST(result == computed_result, "rlwnm %08x, %d, " #mb ", " #me "\n" \
"\tgot: %08x\n" \
"\texpected: %08x\n", \
values[i], sh, result, computed_result); \
} \
} while (0)
RLWNMX_TEST(0, 0);
RLWNMX_TEST(0, 7);
RLWNMX_TEST(0, 15);
RLWNMX_TEST(0, 23);
RLWNMX_TEST(0, 31);
RLWNMX_TEST(7, 0);
RLWNMX_TEST(7, 7);
RLWNMX_TEST(7, 15);
RLWNMX_TEST(7, 23);
RLWNMX_TEST(7, 31);
RLWNMX_TEST(15, 0);
RLWNMX_TEST(15, 7);
RLWNMX_TEST(15, 15);
RLWNMX_TEST(15, 23);
RLWNMX_TEST(15, 31);
RLWNMX_TEST(23, 0);
RLWNMX_TEST(23, 7);
RLWNMX_TEST(23, 15);
RLWNMX_TEST(23, 23);
RLWNMX_TEST(23, 31);
RLWNMX_TEST(31, 0);
RLWNMX_TEST(31, 7);
RLWNMX_TEST(31, 15);
RLWNMX_TEST(31, 23);
RLWNMX_TEST(31, 31);
END_TEST();
RLWNMX_TEST(0, 0);
RLWNMX_TEST(0, 7);
RLWNMX_TEST(0, 15);
RLWNMX_TEST(0, 23);
RLWNMX_TEST(0, 31);
RLWNMX_TEST(7, 0);
RLWNMX_TEST(7, 7);
RLWNMX_TEST(7, 15);
RLWNMX_TEST(7, 23);
RLWNMX_TEST(7, 31);
RLWNMX_TEST(15, 0);
RLWNMX_TEST(15, 7);
RLWNMX_TEST(15, 15);
RLWNMX_TEST(15, 23);
RLWNMX_TEST(15, 31);
RLWNMX_TEST(23, 0);
RLWNMX_TEST(23, 7);
RLWNMX_TEST(23, 15);
RLWNMX_TEST(23, 23);
RLWNMX_TEST(23, 31);
RLWNMX_TEST(31, 0);
RLWNMX_TEST(31, 7);
RLWNMX_TEST(31, 15);
RLWNMX_TEST(31, 23);
RLWNMX_TEST(31, 31);
END_TEST();
}
int main()
{
network_init();
network_init();
rlwimixTest();
rlwinmxTest();
rlwnmxTest();
rlwimixTest();
rlwinmxTest();
rlwnmxTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -1,76 +1,77 @@
#include <gctypes.h>
#include <wiiuse/wpad.h>
#include <stdlib.h>
#include <limits.h>
#include <stdlib.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
static int GetCarry(int value, int shift)
{
if (shift == 0) return 0;
return (value < 0) && (((value >> shift) << shift) != value);
if (shift == 0)
return 0;
return (value < 0) && (((value >> shift) << shift) != value);
}
#define SRAWIX_TEST(shift)\
do\
{\
for(int i = 0; i < 0x1000; i++)\
{\
s32 input = i ? rand() : INT_MIN;\
s32 output = input;\
s32 carry = 0;\
asm(\
"srawi %0, %0, %2;"\
"addze %1, %1;"\
: "+r"(output), "+r"(carry)\
: "i"(shift)\
);\
DO_TEST(carry == GetCarry(input, shift), "(%x >> %d), got carry = %x, expected %x", input, shift, carry, GetCarry(input, shift));\
DO_TEST(output == (input >> shift), "(%x >> %d), got %x, expected %x", input, shift, output, (input >> shift));\
}\
} while(0)
#define SRAWIX_TEST(shift) \
do \
{ \
for (int i = 0; i < 0x1000; i++) \
{ \
s32 input = i ? rand() : INT_MIN; \
s32 output = input; \
s32 carry = 0; \
asm("srawi %0, %0, %2;" \
"addze %1, %1;" \
: "+r"(output), "+r"(carry) \
: "i"(shift)); \
DO_TEST(carry == GetCarry(input, shift), "(%x >> %d), got carry = %x, expected %x", input, \
shift, carry, GetCarry(input, shift)); \
DO_TEST(output == (input >> shift), "(%x >> %d), got %x, expected %x", input, shift, output, \
(input >> shift)); \
} \
} while (0)
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
START_TEST();
SRAWIX_TEST(0);
SRAWIX_TEST(1);
SRAWIX_TEST(2);
SRAWIX_TEST(3);
SRAWIX_TEST(4);
SRAWIX_TEST(5);
SRAWIX_TEST(6);
SRAWIX_TEST(7);
SRAWIX_TEST(8);
SRAWIX_TEST(9);
SRAWIX_TEST(10);
SRAWIX_TEST(11);
SRAWIX_TEST(12);
SRAWIX_TEST(13);
SRAWIX_TEST(14);
SRAWIX_TEST(15);
SRAWIX_TEST(16);
SRAWIX_TEST(17);
SRAWIX_TEST(18);
SRAWIX_TEST(19);
SRAWIX_TEST(20);
SRAWIX_TEST(21);
SRAWIX_TEST(22);
SRAWIX_TEST(23);
SRAWIX_TEST(24);
SRAWIX_TEST(25);
SRAWIX_TEST(26);
SRAWIX_TEST(27);
SRAWIX_TEST(28);
SRAWIX_TEST(29);
SRAWIX_TEST(30);
SRAWIX_TEST(31);
END_TEST();
START_TEST();
SRAWIX_TEST(0);
SRAWIX_TEST(1);
SRAWIX_TEST(2);
SRAWIX_TEST(3);
SRAWIX_TEST(4);
SRAWIX_TEST(5);
SRAWIX_TEST(6);
SRAWIX_TEST(7);
SRAWIX_TEST(8);
SRAWIX_TEST(9);
SRAWIX_TEST(10);
SRAWIX_TEST(11);
SRAWIX_TEST(12);
SRAWIX_TEST(13);
SRAWIX_TEST(14);
SRAWIX_TEST(15);
SRAWIX_TEST(16);
SRAWIX_TEST(17);
SRAWIX_TEST(18);
SRAWIX_TEST(19);
SRAWIX_TEST(20);
SRAWIX_TEST(21);
SRAWIX_TEST(22);
SRAWIX_TEST(23);
SRAWIX_TEST(24);
SRAWIX_TEST(25);
SRAWIX_TEST(26);
SRAWIX_TEST(27);
SRAWIX_TEST(28);
SRAWIX_TEST(29);
SRAWIX_TEST(30);
SRAWIX_TEST(31);
END_TEST();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,304 +11,304 @@
// Vertex array numbers
enum
{
ARRAY_POSITION = 0,
ARRAY_NORMAL = 1,
ARRAY_COLOR = 2,
ARRAY_COLOR2 = 3,
ARRAY_TEXCOORD0 = 4,
ARRAY_POSITION = 0,
ARRAY_NORMAL = 1,
ARRAY_COLOR = 2,
ARRAY_COLOR2 = 3,
ARRAY_TEXCOORD0 = 4,
};
// Vertex components
enum
{
NOT_PRESENT = 0,
DIRECT = 1,
INDEX8 = 2,
INDEX16 = 3,
NOT_PRESENT = 0,
DIRECT = 1,
INDEX8 = 2,
INDEX16 = 3,
};
enum
{
FORMAT_UBYTE = 0, // 2 Cmp
FORMAT_BYTE = 1, // 3 Cmp
FORMAT_USHORT = 2,
FORMAT_SHORT = 3,
FORMAT_FLOAT = 4,
FORMAT_UBYTE = 0, // 2 Cmp
FORMAT_BYTE = 1, // 3 Cmp
FORMAT_USHORT = 2,
FORMAT_SHORT = 3,
FORMAT_FLOAT = 4,
};
enum
{
FORMAT_16B_565 = 0, // NA
FORMAT_24B_888 = 1,
FORMAT_32B_888x = 2,
FORMAT_16B_4444 = 3,
FORMAT_24B_6666 = 4,
FORMAT_32B_8888 = 5,
FORMAT_16B_565 = 0, // NA
FORMAT_24B_888 = 1,
FORMAT_32B_888x = 2,
FORMAT_16B_4444 = 3,
FORMAT_24B_6666 = 4,
FORMAT_32B_8888 = 5,
};
enum
{
VAT_0_FRACBITS = 0x3e0001f0,
VAT_1_FRACBITS = 0x07c3e1f0,
VAT_2_FRACBITS = 0xf87c3e1f,
VAT_0_FRACBITS = 0x3e0001f0,
VAT_1_FRACBITS = 0x07c3e1f0,
VAT_2_FRACBITS = 0xf87c3e1f,
};
enum
{
VA_PTNMTXIDX = 0,
VA_TEX0MTXIDX = 1,
VA_TEX1MTXIDX = 2,
VA_TEX2MTXIDX = 3,
VA_TEX3MTXIDX = 4,
VA_TEX4MTXIDX = 5,
VA_TEX5MTXIDX = 6,
VA_TEX6MTXIDX = 7,
VA_TEX7MTXIDX = 8,
VA_POS = 9,
VA_NRM = 10,
VA_CLR0 = 11,
VA_CLR1 = 12,
VA_TEX0 = 13,
VA_TEX1 = 14,
VA_TEX2 = 15,
VA_TEX3 = 16,
VA_TEX4 = 17,
VA_TEX5 = 18,
VA_TEX6 = 19,
VA_TEX7 = 20,
VA_POSMTXARRAY = 21,
VA_NRMMTXARRAY = 22,
VA_TEXMTXARRAY = 23,
VA_LIGHTARRAY = 24,
VA_NBT = 25,
VA_MAXATTR = 26,
VA_NULL = 0xff
VA_PTNMTXIDX = 0,
VA_TEX0MTXIDX = 1,
VA_TEX1MTXIDX = 2,
VA_TEX2MTXIDX = 3,
VA_TEX3MTXIDX = 4,
VA_TEX4MTXIDX = 5,
VA_TEX5MTXIDX = 6,
VA_TEX6MTXIDX = 7,
VA_TEX7MTXIDX = 8,
VA_POS = 9,
VA_NRM = 10,
VA_CLR0 = 11,
VA_CLR1 = 12,
VA_TEX0 = 13,
VA_TEX1 = 14,
VA_TEX2 = 15,
VA_TEX3 = 16,
VA_TEX4 = 17,
VA_TEX5 = 18,
VA_TEX6 = 19,
VA_TEX7 = 20,
VA_POSMTXARRAY = 21,
VA_NRMMTXARRAY = 22,
VA_TEXMTXARRAY = 23,
VA_LIGHTARRAY = 24,
VA_NBT = 25,
VA_MAXATTR = 26,
VA_NULL = 0xff
};
// Vertex attribute component format
enum
{
VA_FMT_U8 = 0,
VA_FMT_S8 = 1,
VA_FMT_U16 = 2,
VA_FMT_S16 = 3,
VA_FMT_F32 = 4
VA_FMT_U8 = 0,
VA_FMT_S8 = 1,
VA_FMT_U16 = 2,
VA_FMT_S16 = 3,
VA_FMT_F32 = 4
};
enum
{
VA_FMT_RGB565 = 0,
VA_FMT_RGB8 = 1,
VA_FMT_RGBX8 = 2,
VA_FMT_RGBA4 = 3,
VA_FMT_RGBA6 = 4,
VA_FMT_RGBA8 = 5
VA_FMT_RGB565 = 0,
VA_FMT_RGB8 = 1,
VA_FMT_RGBX8 = 2,
VA_FMT_RGBA4 = 3,
VA_FMT_RGBA6 = 4,
VA_FMT_RGBA8 = 5
};
// Vertex attribute component types
enum
{
VA_TYPE_POS_XY = 0,
VA_TYPE_POS_XYZ = 1
VA_TYPE_POS_XY = 0,
VA_TYPE_POS_XYZ = 1
};
enum
{
VA_TYPE_NRM_XYZ = 0,
VA_TYPE_NRM_NBT = 1,
VA_TYPE_NRM_NBT3 = 2
VA_TYPE_NRM_XYZ = 0,
VA_TYPE_NRM_NBT = 1,
VA_TYPE_NRM_NBT3 = 2
};
enum
{
VA_TYPE_CLR_RGB = 0,
VA_TYPE_CLR_RGBA = 1,
VA_TYPE_CLR_RGB = 0,
VA_TYPE_CLR_RGBA = 1,
};
enum
{
VA_TYPE_TEX_S = 0,
VA_TYPE_TEX_ST = 1,
VA_TYPE_TEX_S = 0,
VA_TYPE_TEX_ST = 1,
};
enum
{
VTXATTR_NONE = 0,
VTXATTR_DIRECT = 1,
VTXATTR_INDEX8 = 2,
VTXATTR_INDEX16 = 3
VTXATTR_NONE = 0,
VTXATTR_DIRECT = 1,
VTXATTR_INDEX8 = 2,
VTXATTR_INDEX16 = 3
};
#pragma pack(4)
union TVtxDesc
{
u64 Hex;
u64 Hex;
BitField< 0,32,u64> Hex0;
BitField<32,32,u64> Hex1;
BitField<0, 32, u64> Hex0;
BitField<32, 32, u64> Hex1;
// Note: Access to this array is not endianness-independent.
u8 byte[8];
// Note: Access to this array is not endianness-independent.
u8 byte[8];
BitField<0,1,u64> PosMatIdx;
BitField<1,1,u64> Tex0MatIdx;
BitField<2,1,u64> Tex1MatIdx;
BitField<3,1,u64> Tex2MatIdx;
BitField<4,1,u64> Tex3MatIdx;
BitField<5,1,u64> Tex4MatIdx;
BitField<6,1,u64> Tex5MatIdx;
BitField<7,1,u64> Tex6MatIdx;
BitField<8,1,u64> Tex7MatIdx;
BitField<9,2,u64> Position;
BitField<11,2,u64> Normal;
BitField<13,2,u64> Color0;
BitField<15,2,u64> Color1;
BitField<17,2,u64> Tex0Coord;
BitField<19,2,u64> Tex1Coord;
BitField<21,2,u64> Tex2Coord;
BitField<23,2,u64> Tex3Coord;
BitField<25,2,u64> Tex4Coord;
BitField<27,2,u64> Tex5Coord;
BitField<29,2,u64> Tex6Coord;
BitField<31,2,u64> Tex7Coord;
// 31 unused bits follow
BitField<0, 1, u64> PosMatIdx;
BitField<1, 1, u64> Tex0MatIdx;
BitField<2, 1, u64> Tex1MatIdx;
BitField<3, 1, u64> Tex2MatIdx;
BitField<4, 1, u64> Tex3MatIdx;
BitField<5, 1, u64> Tex4MatIdx;
BitField<6, 1, u64> Tex5MatIdx;
BitField<7, 1, u64> Tex6MatIdx;
BitField<8, 1, u64> Tex7MatIdx;
BitField<9, 2, u64> Position;
BitField<11, 2, u64> Normal;
BitField<13, 2, u64> Color0;
BitField<15, 2, u64> Color1;
BitField<17, 2, u64> Tex0Coord;
BitField<19, 2, u64> Tex1Coord;
BitField<21, 2, u64> Tex2Coord;
BitField<23, 2, u64> Tex3Coord;
BitField<25, 2, u64> Tex4Coord;
BitField<27, 2, u64> Tex5Coord;
BitField<29, 2, u64> Tex6Coord;
BitField<31, 2, u64> Tex7Coord;
// 31 unused bits follow
};
union UVAT_group0
{
BitField<0,1,u32> PosElements;
BitField<1,3,u32> PosFormat;
BitField<4,5,u32> PosFrac;
BitField<0, 1, u32> PosElements;
BitField<1, 3, u32> PosFormat;
BitField<4, 5, u32> PosFrac;
BitField<9,1,u32> NormalElements;
BitField<10,3,u32> NormalFormat;
BitField<9, 1, u32> NormalElements;
BitField<10, 3, u32> NormalFormat;
BitField<13,1,u32> Color0Elements;
BitField<14,3,u32> Color0Comp;
BitField<13, 1, u32> Color0Elements;
BitField<14, 3, u32> Color0Comp;
BitField<17,1,u32> Color1Elements;
BitField<18,3,u32> Color1Comp;
BitField<17, 1, u32> Color1Elements;
BitField<18, 3, u32> Color1Comp;
BitField<21,1,u32> Tex0CoordElements;
BitField<22,3,u32> Tex0CoordFormat;
BitField<25,5,u32> Tex0Frac;
BitField<21, 1, u32> Tex0CoordElements;
BitField<22, 3, u32> Tex0CoordFormat;
BitField<25, 5, u32> Tex0Frac;
BitField<30,1,u32> ByteDequant;
BitField<31,1,u32> NormalIndex3;
BitField<30, 1, u32> ByteDequant;
BitField<31, 1, u32> NormalIndex3;
u32 Hex;
u32 Hex;
};
union UVAT_group1
{
BitField<0,1,u32> Tex1CoordElements;
BitField<1,3,u32> Tex1CoordFormat;
BitField<4,5,u32> Tex1Frac;
BitField<0, 1, u32> Tex1CoordElements;
BitField<1, 3, u32> Tex1CoordFormat;
BitField<4, 5, u32> Tex1Frac;
BitField<9,1,u32> Tex2CoordElements;
BitField<10,3,u32> Tex2CoordFormat;
BitField<13,5,u32> Tex2Frac;
BitField<9, 1, u32> Tex2CoordElements;
BitField<10, 3, u32> Tex2CoordFormat;
BitField<13, 5, u32> Tex2Frac;
BitField<18,1,u32> Tex3CoordElements;
BitField<19,3,u32> Tex3CoordFormat;
BitField<22,5,u32> Tex3Frac;
BitField<18, 1, u32> Tex3CoordElements;
BitField<19, 3, u32> Tex3CoordFormat;
BitField<22, 5, u32> Tex3Frac;
BitField<27,1,u32> Tex4CoordElements;
BitField<28,3,u32> Tex4CoordFormat;
// 1 bit unused
BitField<27, 1, u32> Tex4CoordElements;
BitField<28, 3, u32> Tex4CoordFormat;
// 1 bit unused
u32 Hex;
u32 Hex;
};
union UVAT_group2
{
BitField<0,5,u32> Tex4Frac;
BitField<0, 5, u32> Tex4Frac;
BitField<5,1,u32> Tex5CoordElements;
BitField<6,3,u32> Tex5CoordFormat;
BitField<9,5,u32> Tex5Frac;
BitField<5, 1, u32> Tex5CoordElements;
BitField<6, 3, u32> Tex5CoordFormat;
BitField<9, 5, u32> Tex5Frac;
BitField<14,1,u32> Tex6CoordElements;
BitField<15,3,u32> Tex6CoordFormat;
BitField<18,5,u32> Tex6Frac;
BitField<14, 1, u32> Tex6CoordElements;
BitField<15, 3, u32> Tex6CoordFormat;
BitField<18, 5, u32> Tex6Frac;
BitField<23,1,u32> Tex7CoordElements;
BitField<24,3,u32> Tex7CoordFormat;
BitField<27,5,u32> Tex7Frac;
BitField<23, 1, u32> Tex7CoordElements;
BitField<24, 3, u32> Tex7CoordFormat;
BitField<27, 5, u32> Tex7Frac;
u32 Hex;
u32 Hex;
};
struct ColorAttr
{
u8 Elements;
u8 Comp;
u8 Elements;
u8 Comp;
};
struct TexAttr
{
u8 Elements;
u8 Format;
u8 Frac;
u8 Elements;
u8 Format;
u8 Frac;
};
struct TVtxAttr
{
u8 PosElements;
u8 PosFormat;
u8 PosFrac;
u8 NormalElements;
u8 NormalFormat;
ColorAttr color[2];
TexAttr texCoord[8];
u8 ByteDequant;
u8 NormalIndex3;
u8 PosElements;
u8 PosFormat;
u8 PosFrac;
u8 NormalElements;
u8 NormalFormat;
ColorAttr color[2];
TexAttr texCoord[8];
u8 ByteDequant;
u8 NormalIndex3;
};
// Matrix indices
union TMatrixIndexA
{
BitField<0,6,u32> PosNormalMtxIdx;
BitField<6,6,u32> Tex0MtxIdx;
BitField<12,6,u32> Tex1MtxIdx;
BitField<18,6,u32> Tex2MtxIdx;
BitField<24,6,u32> Tex3MtxIdx;
BitField<0, 6, u32> PosNormalMtxIdx;
BitField<6, 6, u32> Tex0MtxIdx;
BitField<12, 6, u32> Tex1MtxIdx;
BitField<18, 6, u32> Tex2MtxIdx;
BitField<24, 6, u32> Tex3MtxIdx;
BitField<0,30,u32> Hex;
BitField<0, 30, u32> Hex;
};
union TMatrixIndexB
{
BitField<0,6,u32> Tex4MtxIdx;
BitField<6,6,u32> Tex5MtxIdx;
BitField<12,6,u32> Tex6MtxIdx;
BitField<18,6,u32> Tex7MtxIdx;
BitField<0, 6, u32> Tex4MtxIdx;
BitField<6, 6, u32> Tex5MtxIdx;
BitField<12, 6, u32> Tex6MtxIdx;
BitField<18, 6, u32> Tex7MtxIdx;
BitField<0,24,u32> Hex;
BitField<0, 24, u32> Hex;
};
#pragma pack()
extern u32 arraybases[16];
extern u8 *cached_arraybases[16];
extern u8* cached_arraybases[16];
extern u32 arraystrides[16];
extern TMatrixIndexA MatrixIndexA;
extern TMatrixIndexB MatrixIndexB;
struct VAT
{
UVAT_group0 g0;
UVAT_group1 g1;
UVAT_group2 g2;
UVAT_group0 g0;
UVAT_group1 g1;
UVAT_group2 g2;
};
extern TVtxDesc g_VtxDesc;
extern VAT g_VtxAttr[8];
// Might move this into its own file later.
//void LoadCPReg(u32 SubCmd, u32 Value);
// void LoadCPReg(u32 SubCmd, u32 Value);
// Fills memory with data from CP regs
//void FillCPMemoryArray(u32 *memory);
// void FillCPMemoryArray(u32 *memory);
#endif // _CPMEMORY_H
#endif // _CPMEMORY_H

View File

@@ -6,18 +6,18 @@
union LitChannel
{
BitField<0,1,u32> matsource;
BitField<1,1,u32> enablelighting;
BitField<2,4,u32> lightMask0_3;
BitField<6,1,u32> ambsource;
BitField<7,2,u32> diffusefunc; // LIGHTDIF_X
BitField<9,2,u32> attnfunc; // LIGHTATTN_X
BitField<11,4,u32> lightMask4_7;
// 17 bits unused
BitField<0, 1, u32> matsource;
BitField<1, 1, u32> enablelighting;
BitField<2, 4, u32> lightMask0_3;
BitField<6, 1, u32> ambsource;
BitField<7, 2, u32> diffusefunc; // LIGHTDIF_X
BitField<9, 2, u32> attnfunc; // LIGHTATTN_X
BitField<11, 4, u32> lightMask4_7;
// 17 bits unused
u32 hex;
unsigned int GetFullLightMask() const
{
return enablelighting ? (lightMask0_3 | (lightMask4_7 << 4)) : 0;
}
u32 hex;
unsigned int GetFullLightMask() const
{
return enablelighting ? (lightMask0_3 | (lightMask4_7 << 4)) : 0;
}
};

View File

@@ -3,56 +3,56 @@
// Refer to the license.txt file included.
#include <initializer_list>
#include "common/hwtests.h"
#include <math.h>
#include <ogcsys.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
#include "gxtest/util.h"
#include <ogcsys.h>
void BitfieldTest()
{
START_TEST();
START_TEST();
TevReg reg;
reg.hex = 0;
reg.low = 0x345678;
DO_TEST(reg.alpha == 837, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -392, "Values don't match (have: %d)", (s32)reg.red);
reg.low = 0x4BC6A8;
DO_TEST(reg.alpha == -836, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -344, "Values don't match (have: %d)", (s32)reg.red);
reg.hex = 0;
reg.alpha = -263;
reg.red = -345;
DO_TEST(reg.alpha == -263, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -345, "Values don't match (have: %d)", (s32)reg.red);
reg.alpha = 15;
reg.red = -619;
DO_TEST(reg.alpha == 15, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -619, "Values don't match (have: %d)", (s32)reg.red);
reg.alpha = 523;
reg.red = 176;
DO_TEST(reg.alpha == 523, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == 176, "Values don't match (have: %d)", (s32)reg.red);
TevReg reg;
reg.hex = 0;
reg.low = 0x345678;
DO_TEST(reg.alpha == 837, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -392, "Values don't match (have: %d)", (s32)reg.red);
reg.low = 0x4BC6A8;
DO_TEST(reg.alpha == -836, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -344, "Values don't match (have: %d)", (s32)reg.red);
reg.hex = 0;
reg.alpha = -263;
reg.red = -345;
DO_TEST(reg.alpha == -263, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -345, "Values don't match (have: %d)", (s32)reg.red);
reg.alpha = 15;
reg.red = -619;
DO_TEST(reg.alpha == 15, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == -619, "Values don't match (have: %d)", (s32)reg.red);
reg.alpha = 523;
reg.red = 176;
DO_TEST(reg.alpha == 523, "Values don't match (have: %d)", (s32)reg.alpha);
DO_TEST(reg.red == 176, "Values don't match (have: %d)", (s32)reg.red);
END_TEST();
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GXTest::Init();
GXTest::Init();
BitfieldTest();
BitfieldTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -4,12 +4,12 @@
#include <assert.h>
#include <malloc.h>
#include <string.h>
#include <ogc/system.h>
#include <ogc/cache.h>
#include <ogc/gx.h>
#include <ogc/irq.h>
#include <ogc/machine/processor.h>
#include <ogc/system.h>
#include <string.h>
#include "common/CommonTypes.h"
#include "gxtest/BPMemory.h"
@@ -22,82 +22,83 @@ typedef float f32;
typedef union
{
volatile u8 U8;
volatile s8 S8;
volatile u16 U16;
volatile s16 S16;
volatile u32 U32;
volatile s32 S32;
volatile f32 F32;
volatile u8 U8;
volatile s8 S8;
volatile u16 U16;
volatile s16 S16;
volatile u32 U32;
volatile s32 S32;
volatile f32 F32;
} CWGPipe;
//static CWGPipe* const wgPipe = (CWGPipe*)0xCC008000;
// static CWGPipe* const wgPipe = (CWGPipe*)0xCC008000;
// TODO: Get rid of these definitions!
//struct GXFifoObj;
extern "C"
{
// struct GXFifoObj;
extern "C" {
GXFifoObj* GX_Init(void* base, u32 size);
}
static void __CGXFinishInterruptHandler(u32 irq,void *ctx);
static void __CGXFinishInterruptHandler(u32 irq, void* ctx);
static vu16* const _peReg = (u16*)0xCC001000;
static lwpq_t _cgxwaitfinish;
static vu32 _cgxfinished = 0;
#define CGX_LOAD_BP_REG(x) \
do { \
wgPipe->U8 = 0x61; \
wgPipe->U32 = (u32)(x); \
} while(0)
#define CGX_LOAD_BP_REG(x) \
do \
{ \
wgPipe->U8 = 0x61; \
wgPipe->U32 = (u32)(x); \
} while (0)
#define CGX_LOAD_CP_REG(x, y) \
do { \
wgPipe->U8 = 0x08; \
wgPipe->U8 = (u8)(x); \
wgPipe->U32 = (u32)(y); \
} while(0)
#define CGX_LOAD_CP_REG(x, y) \
do \
{ \
wgPipe->U8 = 0x08; \
wgPipe->U8 = (u8)(x); \
wgPipe->U32 = (u32)(y); \
} while (0)
#define CGX_BEGIN_LOAD_XF_REGS(x, n) \
do { \
wgPipe->U8 = 0x10; \
wgPipe->U32 = (u32)(((((n)&0xffff)-1)<<16)|((x)&0xffff)); \
} while(0)
#define CGX_BEGIN_LOAD_XF_REGS(x, n) \
do \
{ \
wgPipe->U8 = 0x10; \
wgPipe->U32 = (u32)(((((n)&0xffff) - 1) << 16) | ((x)&0xffff)); \
} while (0)
void CGX_Init()
{
// TODO: Is this leaking memory?
void *gp_fifo = NULL;
gp_fifo = memalign(32, 256*1024);
memset(gp_fifo, 0, 256*1024);
// TODO: Is this leaking memory?
void* gp_fifo = NULL;
gp_fifo = memalign(32, 256 * 1024);
memset(gp_fifo, 0, 256 * 1024);
GX_Init(gp_fifo, 256*1024);
GX_Init(gp_fifo, 256 * 1024);
LWP_InitQueue(&_cgxwaitfinish);
LWP_InitQueue(&_cgxwaitfinish);
IRQ_Request(IRQ_PI_PEFINISH,__CGXFinishInterruptHandler,NULL);
__UnmaskIrq(IRQMASK(IRQ_PI_PEFINISH));
_peReg[5] = 0x0F;
IRQ_Request(IRQ_PI_PEFINISH, __CGXFinishInterruptHandler, NULL);
__UnmaskIrq(IRQMASK(IRQ_PI_PEFINISH));
_peReg[5] = 0x0F;
}
void CGX_SetViewport(float origin_x, float origin_y, float width, float height, float near, f32 far)
{
CGX_BEGIN_LOAD_XF_REGS(0x101a,6);
wgPipe->F32 = width*0.5f;
wgPipe->F32 = -height*0.5f;
wgPipe->F32 = (far-near)*16777215.0f;
wgPipe->F32 = 342.0f+origin_x+width*0.5f;
wgPipe->F32 = 342.0f+origin_y+height*0.5f;
wgPipe->F32 = far*16777215.0f;
CGX_BEGIN_LOAD_XF_REGS(0x101a, 6);
wgPipe->F32 = width * 0.5f;
wgPipe->F32 = -height * 0.5f;
wgPipe->F32 = (far - near) * 16777215.0f;
wgPipe->F32 = 342.0f + origin_x + width * 0.5f;
wgPipe->F32 = 342.0f + origin_y + height * 0.5f;
wgPipe->F32 = far * 16777215.0f;
}
static inline void WriteMtxPS4x2(register f32 mt[3][4], register void* wgpipe)
{
// Untested
register f32 tmp0, tmp1, tmp2, tmp3;
// Untested
register f32 tmp0, tmp1, tmp2, tmp3;
__asm__ __volatile__
("psq_l %0,0(%4),0,0\n\
__asm__ __volatile__("psq_l %0,0(%4),0,0\n\
psq_l %1,8(%4),0,0\n\
psq_l %2,16(%4),0,0\n\
psq_l %3,24(%4),0,0\n\
@@ -105,151 +106,152 @@ static inline void WriteMtxPS4x2(register f32 mt[3][4], register void* wgpipe)
psq_st %1,0(%5),0,0\n\
psq_st %2,0(%5),0,0\n\
psq_st %3,0(%5),0,0"
: "=&f"(tmp0),"=&f"(tmp1),"=&f"(tmp2),"=&f"(tmp3)
: "b"(mt), "b"(wgpipe)
: "memory"
);
: "=&f"(tmp0), "=&f"(tmp1), "=&f"(tmp2), "=&f"(tmp3)
: "b"(mt), "b"(wgpipe)
: "memory");
}
void CGX_LoadPosMatrixDirect(f32 mt[3][4], u32 index)
{
// Untested
/* CGX_BEGIN_LOAD_XF_REGS((index<<2)&0xFF, 12);
WriteMtxPS4x2(mt, (void*)wgPipe);*/
GX_LoadPosMtxImm(mt, index);
// Untested
/* CGX_BEGIN_LOAD_XF_REGS((index<<2)&0xFF, 12);
WriteMtxPS4x2(mt, (void*)wgPipe);*/
GX_LoadPosMtxImm(mt, index);
}
void CGX_LoadProjectionMatrixPerspective(float mtx[4][4])
{
// Untested
/* CGX_BEGIN_LOAD_XF_REGS(0x1020, 7);
wgPipe->F32 = mtx[0][0];
wgPipe->F32 = mtx[0][2];
wgPipe->F32 = mtx[1][1];
wgPipe->F32 = mtx[1][2];
wgPipe->F32 = mtx[2][2];
wgPipe->F32 = mtx[2][3];
wgPipe->U32 = 0;*/
GX_LoadProjectionMtx(mtx, 0);
// Untested
/* CGX_BEGIN_LOAD_XF_REGS(0x1020, 7);
wgPipe->F32 = mtx[0][0];
wgPipe->F32 = mtx[0][2];
wgPipe->F32 = mtx[1][1];
wgPipe->F32 = mtx[1][2];
wgPipe->F32 = mtx[2][2];
wgPipe->F32 = mtx[2][3];
wgPipe->U32 = 0;*/
GX_LoadProjectionMtx(mtx, 0);
}
void CGX_LoadProjectionMatrixOrthographic(float mtx[4][4])
{
// Untested
/* CGX_BEGIN_LOAD_XF_REGS(0x1020, 7);
wgPipe->F32 = mtx[0][0];
wgPipe->F32 = mtx[0][3];
wgPipe->F32 = mtx[1][1];
wgPipe->F32 = mtx[1][3];
wgPipe->F32 = mtx[2][2];
wgPipe->F32 = mtx[2][3];
wgPipe->U32 = 1;*/
GX_LoadProjectionMtx(mtx, 1);
// Untested
/* CGX_BEGIN_LOAD_XF_REGS(0x1020, 7);
wgPipe->F32 = mtx[0][0];
wgPipe->F32 = mtx[0][3];
wgPipe->F32 = mtx[1][1];
wgPipe->F32 = mtx[1][3];
wgPipe->F32 = mtx[2][2];
wgPipe->F32 = mtx[2][3];
wgPipe->U32 = 1;*/
GX_LoadProjectionMtx(mtx, 1);
}
void CGX_DoEfbCopyTex(u16 left, u16 top, u16 width, u16 height, u8 dest_format, bool copy_to_intensity, void* dest, bool scale_down, bool clear)
void CGX_DoEfbCopyTex(u16 left, u16 top, u16 width, u16 height, u8 dest_format,
bool copy_to_intensity, void* dest, bool scale_down, bool clear)
{
assert(left <= 1023);
assert(top <= 1023);
assert(width <= 1023);
assert(height <= 1023);
assert(left <= 1023);
assert(top <= 1023);
assert(width <= 1023);
assert(height <= 1023);
// TODO: GX_TF_Z16 seems to have special treatment in libogc? oO
// TODO: GX_TF_Z16 seems to have special treatment in libogc? oO
X10Y10 coords;
coords.hex = BPMEM_EFB_TL << 24;
coords.x = left;
coords.y = top;
CGX_LOAD_BP_REG(coords.hex);
X10Y10 coords;
coords.hex = BPMEM_EFB_TL << 24;
coords.x = left;
coords.y = top;
CGX_LOAD_BP_REG(coords.hex);
coords.hex = BPMEM_EFB_BR << 24;
coords.x = width - 1;
coords.y = height - 1;
CGX_LOAD_BP_REG(coords.hex);
coords.hex = BPMEM_EFB_BR << 24;
coords.x = width - 1;
coords.y = height - 1;
CGX_LOAD_BP_REG(coords.hex);
// TODO: this one is hardcoded against dest_format=RGBA8...
CGX_LOAD_BP_REG((BPMEM_MIPMAP_STRIDE << 24) | (((width+3)>>2) * 2));
// TODO: this one is hardcoded against dest_format=RGBA8...
CGX_LOAD_BP_REG((BPMEM_MIPMAP_STRIDE << 24) | (((width + 3) >> 2) * 2));
CGX_LOAD_BP_REG((BPMEM_EFB_ADDR<<24) | (MEM_VIRTUAL_TO_PHYSICAL(dest)>>5));
CGX_LOAD_BP_REG((BPMEM_EFB_ADDR << 24) | (MEM_VIRTUAL_TO_PHYSICAL(dest) >> 5));
UPE_Copy reg;
reg.Hex = BPMEM_TRIGGER_EFB_COPY<<24;
reg.target_pixel_format = ((dest_format << 1) & 0xE) | (dest_format >> 3);
reg.half_scale = scale_down;
reg.clear = clear;
reg.intensity_fmt = copy_to_intensity;
reg.clamp0 = 1;
reg.clamp1 = 1;
CGX_LOAD_BP_REG(reg.Hex);
UPE_Copy reg;
reg.Hex = BPMEM_TRIGGER_EFB_COPY << 24;
reg.target_pixel_format = ((dest_format << 1) & 0xE) | (dest_format >> 3);
reg.half_scale = scale_down;
reg.clear = clear;
reg.intensity_fmt = copy_to_intensity;
reg.clamp0 = 1;
reg.clamp1 = 1;
CGX_LOAD_BP_REG(reg.Hex);
DCFlushRange(dest, GX_GetTexBufferSize(width,height,GX_TF_RGBA8,GX_FALSE,1));
DCFlushRange(dest, GX_GetTexBufferSize(width, height, GX_TF_RGBA8, GX_FALSE, 1));
}
void CGX_DoEfbCopyXfb(u16 left, u16 top, u16 width, u16 src_height, u16 dst_height, void* dest, bool clear)
void CGX_DoEfbCopyXfb(u16 left, u16 top, u16 width, u16 src_height, u16 dst_height, void* dest,
bool clear)
{
assert(left <= 1023);
assert(top <= 1023);
assert(width <= 1023);
assert(src_height <= 1023);
assert(left <= 1023);
assert(top <= 1023);
assert(width <= 1023);
assert(src_height <= 1023);
/* UPE_Copy reg;
reg.Hex = BPMEM_TRIGGER_EFB_COPY<<24;
reg.clear = clear;
reg.copy_to_xfb = 1;
/* UPE_Copy reg;
reg.Hex = BPMEM_TRIGGER_EFB_COPY<<24;
reg.clear = clear;
reg.copy_to_xfb = 1;
X10Y10 coords;
coords.hex = 0;
coords.x = left;
coords.y = top;
CGX_LOAD_BP_REG((BPMEM_EFB_TL << 24) | coords.hex);
coords.x = width - 1;
coords.y = height - 1;
CGX_LOAD_BP_REG((BPMEM_EFB_BR << 24) | coords.hex);
X10Y10 coords;
coords.hex = 0;
coords.x = left;
coords.y = top;
CGX_LOAD_BP_REG((BPMEM_EFB_TL << 24) | coords.hex);
coords.x = width - 1;
coords.y = height - 1;
CGX_LOAD_BP_REG((BPMEM_EFB_BR << 24) | coords.hex);
CGX_LOAD_BP_REG((BPMEM_EFB_ADDR<<24) | (MEM_VIRTUAL_TO_PHYSICAL(dest)>>5));
CGX_LOAD_BP_REG((BPMEM_EFB_ADDR<<24) | (MEM_VIRTUAL_TO_PHYSICAL(dest)>>5));
CGX_LOAD_BP_REG((BPMEM_MIPMAP_STRIDE<<24) | (width >> 4));
CGX_LOAD_BP_REG(reg.Hex);*/
CGX_LOAD_BP_REG((BPMEM_MIPMAP_STRIDE<<24) | (width >> 4));
CGX_LOAD_BP_REG(reg.Hex);*/
GX_SetDispCopySrc(left, top, width, src_height);
GX_SetDispCopyDst(width, dst_height);
// SetCopyFilter, SetFieldMode, SetDispCopyGamma
GX_CopyDisp(dest, clear);
GX_SetDispCopySrc(left, top, width, src_height);
GX_SetDispCopyDst(width, dst_height);
// SetCopyFilter, SetFieldMode, SetDispCopyGamma
GX_CopyDisp(dest, clear);
}
void CGX_ForcePipelineFlush()
{
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
wgPipe->U32 = 0;
}
static void __CGXFinishInterruptHandler(u32 irq,void *ctx)
static void __CGXFinishInterruptHandler(u32 irq, void* ctx)
{
_peReg[5] = (_peReg[5]&~0x08)|0x08;
_cgxfinished = 1;
_peReg[5] = (_peReg[5] & ~0x08) | 0x08;
_cgxfinished = 1;
LWP_ThreadBroadcast(_cgxwaitfinish);
LWP_ThreadBroadcast(_cgxwaitfinish);
}
void CGX_WaitForGpuToFinish()
{
u32 level;
u32 level;
_CPU_ISR_Disable(level);
CGX_LOAD_BP_REG(0x45000002); // draw done
CGX_ForcePipelineFlush();
_CPU_ISR_Disable(level);
CGX_LOAD_BP_REG(0x45000002); // draw done
CGX_ForcePipelineFlush();
_cgxfinished = 0;
_CPU_ISR_Flash(level);
_cgxfinished = 0;
_CPU_ISR_Flash(level);
while(!_cgxfinished)
LWP_ThreadSleep(_cgxwaitfinish);
while (!_cgxfinished)
LWP_ThreadSleep(_cgxwaitfinish);
_CPU_ISR_Restore(level);
_CPU_ISR_Restore(level);
}

View File

@@ -21,49 +21,56 @@
typedef union
{
volatile u8 U8;
volatile s8 S8;
volatile u16 U16;
volatile s16 S16;
volatile u32 U32;
volatile s32 S32;
volatile f32 F32;
volatile u8 U8;
volatile s8 S8;
volatile u16 U16;
volatile s16 S16;
volatile u32 U32;
volatile s32 S32;
volatile f32 F32;
} CWGPipe;
static CWGPipe* const wgPipe = (CWGPipe*)0xCC008000;
*/
#define CGX_LOAD_BP_REG(x) \
do { \
wgPipe->U8 = 0x61; \
wgPipe->U32 = (u32)(x); \
} while(0)
#define CGX_LOAD_BP_REG(x) \
do \
{ \
wgPipe->U8 = 0x61; \
wgPipe->U32 = (u32)(x); \
} while (0)
#define CGX_LOAD_CP_REG(x, y) \
do { \
wgPipe->U8 = 0x08; \
wgPipe->U8 = (u8)(x); \
wgPipe->U32 = (u32)(y); \
} while(0)
#define CGX_LOAD_CP_REG(x, y) \
do \
{ \
wgPipe->U8 = 0x08; \
wgPipe->U8 = (u8)(x); \
wgPipe->U32 = (u32)(y); \
} while (0)
#define CGX_BEGIN_LOAD_XF_REGS(x, n) \
do { \
wgPipe->U8 = 0x10; \
wgPipe->U32 = (u32)(((((n)&0xffff)-1)<<16)|((x)&0xffff)); \
} while(0)
#define CGX_BEGIN_LOAD_XF_REGS(x, n) \
do \
{ \
wgPipe->U8 = 0x10; \
wgPipe->U32 = (u32)(((((n)&0xffff) - 1) << 16) | ((x)&0xffff)); \
} while (0)
void CGX_Init();
void CGX_SetViewport(float origin_x, float origin_y, float width, float height, float near, f32 far);
void CGX_SetViewport(float origin_x, float origin_y, float width, float height, float near,
f32 far);
void CGX_LoadPosMatrixDirect(f32 mt[3][4], u32 index);
void CGX_LoadProjectionMatrixPerspective(float mtx[4][4]);
void CGX_LoadProjectionMatrixOrthographic(float mtx[4][4]);
void CGX_DoEfbCopyTex(u16 left, u16 top, u16 width, u16 height, u8 dest_format, bool copy_to_intensity, void* dest, bool scale_down=false, bool clear=false);
void CGX_DoEfbCopyTex(u16 left, u16 top, u16 width, u16 height, u8 dest_format,
bool copy_to_intensity, void* dest, bool scale_down = false,
bool clear = false);
// TODO: Add support for other parameters...
void CGX_DoEfbCopyXfb(u16 left, u16 top, u16 width, u16 src_height, u16 dst_height, void* dest, bool clear=false);
void CGX_DoEfbCopyXfb(u16 left, u16 top, u16 width, u16 src_height, u16 dst_height, void* dest,
bool clear = false);
void CGX_ForcePipelineFlush();

View File

@@ -8,95 +8,94 @@
#include "gxtest/CPMemory.h"
#include "gxtest/XFMemory.h"
template<typename T>
template <typename T>
static T CGXDefault();
template<typename T>
template <typename T>
static T CGXDefault(int);
template<typename T>
template <typename T>
static T CGXDefault(int, bool);
template<>
template <>
GenMode CGXDefault<GenMode>()
{
GenMode genmode;
genmode.hex = BPMEM_GENMODE<<24;
genmode.numtexgens = 0;
genmode.numcolchans = 1;
genmode.numtevstages = 0; // One stage
genmode.cullmode = 0; // No culling
genmode.numindstages = 0;
genmode.zfreeze = 0;
return genmode;
GenMode genmode;
genmode.hex = BPMEM_GENMODE << 24;
genmode.numtexgens = 0;
genmode.numcolchans = 1;
genmode.numtevstages = 0; // One stage
genmode.cullmode = 0; // No culling
genmode.numindstages = 0;
genmode.zfreeze = 0;
return genmode;
}
template<>
template <>
ZMode CGXDefault<ZMode>()
{
ZMode zmode;
zmode.hex = BPMEM_ZMODE << 24;
zmode.testenable = 0;
zmode.func = COMPARE_ALWAYS;
zmode.updateenable = 0;
return zmode;
ZMode zmode;
zmode.hex = BPMEM_ZMODE << 24;
zmode.testenable = 0;
zmode.func = COMPARE_ALWAYS;
zmode.updateenable = 0;
return zmode;
}
template<>
template <>
TevStageCombiner::ColorCombiner CGXDefault<TevStageCombiner::ColorCombiner>(int stage)
{
TevStageCombiner::ColorCombiner cc;
cc.hex = (BPMEM_TEV_COLOR_ENV+2*stage)<<24;
cc.a = TEVCOLORARG_ZERO;
cc.b = TEVCOLORARG_ZERO;
cc.c = TEVCOLORARG_ZERO;
cc.d = TEVCOLORARG_ZERO;
cc.op = TEVOP_ADD;
cc.bias = 0;
cc.shift = TEVSCALE_1;
cc.clamp = 0;
cc.dest = GX_TEVPREV;
return cc;
TevStageCombiner::ColorCombiner cc;
cc.hex = (BPMEM_TEV_COLOR_ENV + 2 * stage) << 24;
cc.a = TEVCOLORARG_ZERO;
cc.b = TEVCOLORARG_ZERO;
cc.c = TEVCOLORARG_ZERO;
cc.d = TEVCOLORARG_ZERO;
cc.op = TEVOP_ADD;
cc.bias = 0;
cc.shift = TEVSCALE_1;
cc.clamp = 0;
cc.dest = GX_TEVPREV;
return cc;
}
template<>
template <>
TevStageCombiner::AlphaCombiner CGXDefault<TevStageCombiner::AlphaCombiner>(int stage)
{
TevStageCombiner::AlphaCombiner ac;
ac.hex = (BPMEM_TEV_ALPHA_ENV+2*stage)<<24;
ac.a = TEVALPHAARG_ZERO;
ac.b = TEVALPHAARG_ZERO;
ac.c = TEVALPHAARG_ZERO;
ac.d = TEVALPHAARG_ZERO;
ac.op = TEVOP_ADD;
ac.bias = 0;
ac.shift = TEVSCALE_1;
ac.clamp = 0;
ac.dest = GX_TEVPREV;
return ac;
TevStageCombiner::AlphaCombiner ac;
ac.hex = (BPMEM_TEV_ALPHA_ENV + 2 * stage) << 24;
ac.a = TEVALPHAARG_ZERO;
ac.b = TEVALPHAARG_ZERO;
ac.c = TEVALPHAARG_ZERO;
ac.d = TEVALPHAARG_ZERO;
ac.op = TEVOP_ADD;
ac.bias = 0;
ac.shift = TEVSCALE_1;
ac.clamp = 0;
ac.dest = GX_TEVPREV;
return ac;
}
template<>
template <>
TwoTevStageOrders CGXDefault<TwoTevStageOrders>(int index)
{
TwoTevStageOrders orders;
orders.hex = (BPMEM_TREF+index)<<24;
orders.texmap0 = GX_TEXMAP_NULL;
orders.texcoord0 = GX_TEXCOORDNULL;
orders.enable0 = 0;
orders.colorchan0 = 0; // equivalent to GX_COLOR0A0
return orders;
TwoTevStageOrders orders;
orders.hex = (BPMEM_TREF + index) << 24;
orders.texmap0 = GX_TEXMAP_NULL;
orders.texcoord0 = GX_TEXCOORDNULL;
orders.enable0 = 0;
orders.colorchan0 = 0; // equivalent to GX_COLOR0A0
return orders;
}
template<>
template <>
TevReg CGXDefault<TevReg>(int index, bool is_konst_color)
{
TevReg tevreg;
tevreg.hex = 0;
tevreg.low = (BPMEM_TEV_REGISTER_L+2*index)<<24;
tevreg.high = (BPMEM_TEV_REGISTER_H+2*index)<<24;
tevreg.type_ra = is_konst_color;
tevreg.type_bg = is_konst_color;
return tevreg;
TevReg tevreg;
tevreg.hex = 0;
tevreg.low = (BPMEM_TEV_REGISTER_L + 2 * index) << 24;
tevreg.high = (BPMEM_TEV_REGISTER_H + 2 * index) << 24;
tevreg.type_ra = is_konst_color;
tevreg.type_bg = is_konst_color;
return tevreg;
}

View File

@@ -3,239 +3,246 @@
// Refer to the license.txt file included.
#include <initializer_list>
#include "common/hwtests.h"
#include <math.h>
#include <ogcsys.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
#include "gxtest/util.h"
#include <ogcsys.h>
void ClipTest()
{
START_TEST();
START_TEST();
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
CGX_LOAD_BP_REG(CGXDefault<TevStageCombiner::AlphaCombiner>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TevStageCombiner::AlphaCombiner>(0).hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE<<24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
for (int step = 0; step < 16; ++step)
{
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
for (int step = 0; step < 16; ++step)
{
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
// First off, clear previous screen contents
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f); // stuff which really should not be filled
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0,0,0,0xff).Draw();
// First off, clear previous screen contents
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f,
1.0f); // stuff which really should not be filled
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
CGX_SetViewport(75.0f, 0.0f, 100.0f, 50.0f, 0.0f, 1.0f); // guardband
cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0,0x7f,0,0xff).Draw();
CGX_SetViewport(75.0f, 0.0f, 100.0f, 50.0f, 0.0f, 1.0f); // guardband
cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0, 0x7f, 0, 0xff).Draw();
CGX_SetViewport(100.0f, 0.0f, 50.0f, 50.0f, 0.0f, 1.0f); // viewport
cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0,0xff,0,0xff).Draw();
CGX_SetViewport(100.0f, 0.0f, 50.0f, 50.0f, 0.0f, 1.0f); // viewport
cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0, 0xff, 0, 0xff).Draw();
// Now, enable testing viewport and draw the (red) testing quad
CGX_SetViewport(100.0f, 0.0f, 50.0f, 50.0f, 0.0f, 1.0f);
// Now, enable testing viewport and draw the (red) testing quad
CGX_SetViewport(100.0f, 0.0f, 50.0f, 50.0f, 0.0f, 1.0f);
cc.d = TEVCOLORARG_C0;
CGX_LOAD_BP_REG(cc.hex);
cc.d = TEVCOLORARG_C0;
CGX_LOAD_BP_REG(cc.hex);
auto tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 0xff;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
auto tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 0xff;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
bool expect_quad_to_be_drawn = true;
int test_x = 125, test_y = 25; // Somewhere within the viewport
GXTest::Quad test_quad;
test_quad.ColorRGBA(0xff,0xff,0xff,0xff);
bool expect_quad_to_be_drawn = true;
int test_x = 125, test_y = 25; // Somewhere within the viewport
GXTest::Quad test_quad;
test_quad.ColorRGBA(0xff, 0xff, 0xff, 0xff);
switch (step)
{
// Rendering outside the viewport when scissor rect is bigger than viewport
// TODO: What about partially covered primitives?
switch (step)
{
// Rendering outside the viewport when scissor rect is bigger than viewport
// TODO: What about partially covered primitives?
case 0: // all vertices within viewport
// Nothing to do
break;
case 0: // all vertices within viewport
// Nothing to do
break;
case 1: // two vertices outside viewport, but within guardband
test_quad.VertexTopLeft(-1.8f, 1.0f, 1.0f).VertexBottomLeft(-1.8f, -1.0f, 1.0f);
test_x = 75; // TODO: Move closer to actual viewport, but debug readback issues first
break;
case 1: // two vertices outside viewport, but within guardband
test_quad.VertexTopLeft(-1.8f, 1.0f, 1.0f).VertexBottomLeft(-1.8f, -1.0f, 1.0f);
test_x = 75; // TODO: Move closer to actual viewport, but debug readback issues first
break;
case 2: // two vertices outside viewport and guardband
test_quad.VertexTopLeft(-2.5f, 1.0f, 1.0f).VertexBottomLeft(-2.5f, -1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
// TODO: Check x=50, should be green
break;
case 2: // two vertices outside viewport and guardband
test_quad.VertexTopLeft(-2.5f, 1.0f, 1.0f).VertexBottomLeft(-2.5f, -1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
// TODO: Check x=50, should be green
break;
case 3: // all vertices outside viewport, but within guardband and NOT on the same side of the
// viewport
test_quad.VertexTopLeft(-1.5f, 1.0f, 1.0f).VertexBottomLeft(-1.5f, -1.0f, 1.0f);
test_quad.VertexTopRight(1.5f, 1.0f, 1.0f).VertexBottomRight(1.5f, 1.0f, 1.0f);
test_x = 80; // TODO: Move closer to actual viewport
break;
case 3: // all vertices outside viewport, but within guardband and NOT on the same side of the viewport
test_quad.VertexTopLeft(-1.5f, 1.0f, 1.0f).VertexBottomLeft(-1.5f, -1.0f, 1.0f);
test_quad.VertexTopRight(1.5f, 1.0f, 1.0f).VertexBottomRight(1.5f, 1.0f, 1.0f);
test_x = 80; // TODO: Move closer to actual viewport
break;
case 4: // all vertices outside viewport and guardband, but NOT on the same side of the
// viewport
test_quad.VertexTopLeft(-2.5f, 1.0f, 1.0f).VertexBottomLeft(-2.5f, -1.0f, 1.0f);
test_quad.VertexTopRight(2.5f, 1.0f, 1.0f).VertexBottomRight(2.5f, 1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
// TODO: Check x=50,x=200?,x=201?, 50 and 201 should be green, 200 should be red
break;
case 4: // all vertices outside viewport and guardband, but NOT on the same side of the viewport
test_quad.VertexTopLeft(-2.5f, 1.0f, 1.0f).VertexBottomLeft(-2.5f, -1.0f, 1.0f);
test_quad.VertexTopRight(2.5f, 1.0f, 1.0f).VertexBottomRight(2.5f, 1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
// TODO: Check x=50,x=200?,x=201?, 50 and 201 should be green, 200 should be red
break;
case 5: // all vertices outside viewport, but within guardband and on the same side of the
// viewport
test_quad.VertexTopLeft(-1.8f, 1.0f, 1.0f).VertexBottomLeft(-1.8f, -1.0f, 1.0f);
test_quad.VertexTopRight(-1.2f, 1.0f, 1.0f).VertexBottomRight(-1.2f, 1.0f, 1.0f);
test_quad.VertexTopRight(1.5f, 1.0f, 1.0f);
expect_quad_to_be_drawn = false;
break;
case 5: // all vertices outside viewport, but within guardband and on the same side of the viewport
test_quad.VertexTopLeft(-1.8f, 1.0f, 1.0f).VertexBottomLeft(-1.8f, -1.0f, 1.0f);
test_quad.VertexTopRight(-1.2f, 1.0f, 1.0f).VertexBottomRight(-1.2f, 1.0f, 1.0f);
test_quad.VertexTopRight(1.5f, 1.0f, 1.0f);
expect_quad_to_be_drawn = false;
break;
case 6: // guardband-clipping test
// TODO: Currently broken
// Exceeds the guard-band clipping plane by the viewport width,
// so the primitive will get clipped such that one edge touches
// the clipping plane.exactly at the vertical viewport center.
// ASCII picture of clipped primitive (within guard-band region):
// |----- pixel row 0
// | pixel row 1
// | pixel row 2
// | pixel row 3
// | pixel row 4
// \ pixel row 5 <-- vertical viewport center
// \ pixel row 6
// \ pixel row 7
// \ pixel row 8
// \ pixel row 9
// \ pixel row 10
test_quad.VertexTopLeft(-4.0f, 1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
test_y = 1;
// TODO: Test y roughly equals 60 (there's no good way to test this without relying on
// pixel-perfect clipping), here should NOT be a quad!
break;
case 6: // guardband-clipping test
// TODO: Currently broken
// Exceeds the guard-band clipping plane by the viewport width,
// so the primitive will get clipped such that one edge touches
// the clipping plane.exactly at the vertical viewport center.
// ASCII picture of clipped primitive (within guard-band region):
// |----- pixel row 0
// | pixel row 1
// | pixel row 2
// | pixel row 3
// | pixel row 4
// \ pixel row 5 <-- vertical viewport center
// \ pixel row 6
// \ pixel row 7
// \ pixel row 8
// \ pixel row 9
// \ pixel row 10
test_quad.VertexTopLeft(-4.0f, 1.0f, 1.0f);
test_x = 51; // TODO: This is actually outside the guardband
test_y = 1;
// TODO: Test y roughly equals 60 (there's no good way to test this without relying on pixel-perfect clipping), here should NOT be a quad!
break;
// Depth clipping tests
case 7: // Everything behind z=w plane, depth clipping enabled
case 8: // Everything behind z=w plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 7; // 0 = enable clipping, 1 = disable clipping
// Depth clipping tests
case 7: // Everything behind z=w plane, depth clipping enabled
case 8: // Everything behind z=w plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 7; // 0 = enable clipping, 1 = disable clipping
test_quad.AtDepth(1.1);
expect_quad_to_be_drawn = false;
break;
test_quad.AtDepth(1.1);
expect_quad_to_be_drawn = false;
break;
case 9: // Everything in front of z=0 plane, depth clipping enabled
case 10: // Everything in front of z=0 plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 9; // 0 = enable clipping, 1 = disable clipping
case 9: // Everything in front of z=0 plane, depth clipping enabled
case 10: // Everything in front of z=0 plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 9; // 0 = enable clipping, 1 = disable clipping
test_quad.AtDepth(-0.00001);
expect_quad_to_be_drawn = false;
break;
test_quad.AtDepth(-0.00001);
expect_quad_to_be_drawn = false;
break;
case 11: // Very slightly behind z=w plane, depth clipping enabled
case 12: // Very slightly behind z=w plane, depth clipping disabled
// TODO: For whatever reason, this doesn't actually work, yet
// The GC/Wii GPU doesn't implement IEEE floats strictly, hence
// the sum of the projected position's z and w is a very small
// number, which by IEEE would be non-zero but which in fact is
// treated as zero.
// In particular, the value by IEEE is -0.00000011920928955078125.
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 11; // 0 = enable clipping, 1 = disable clipping
case 11: // Very slightly behind z=w plane, depth clipping enabled
case 12: // Very slightly behind z=w plane, depth clipping disabled
// TODO: For whatever reason, this doesn't actually work, yet
// The GC/Wii GPU doesn't implement IEEE floats strictly, hence
// the sum of the projected position's z and w is a very small
// number, which by IEEE would be non-zero but which in fact is
// treated as zero.
// In particular, the value by IEEE is -0.00000011920928955078125.
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 11; // 0 = enable clipping, 1 = disable clipping
test_quad.AtDepth(1.0000001);
break;
test_quad.AtDepth(1.0000001);
break;
case 13: // One vertex behind z=w plane, depth clipping enabled
case 14: // One vertex behind z=w plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 13; // 0 = enable clipping, 1 = disable clipping
case 13: // One vertex behind z=w plane, depth clipping enabled
case 14: // One vertex behind z=w plane, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = step - 13; // 0 = enable clipping, 1 = disable clipping
test_quad.VertexTopLeft(-1.0f, 1.0f, 1.5f);
test_quad.VertexTopLeft(-1.0f, 1.0f, 1.5f);
// whole primitive gets clipped away if depth clipping is enabled.
// Otherwise the whole primitive is drawn.
expect_quad_to_be_drawn = step - 13;
break;
// whole primitive gets clipped away if depth clipping is enabled.
// Otherwise the whole primitive is drawn.
expect_quad_to_be_drawn = step - 13;
break;
case 15: // Three vertices with a very large value for z, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 1; // 0 = enable clipping, 1 = disable clipping
case 15: // Three vertices with a very large value for z, depth clipping disabled
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 1; // 0 = enable clipping, 1 = disable clipping
test_quad.VertexTopLeft(-1.0f, 1.0f, 65537.f);
test_quad.VertexTopRight(1.0f, 1.0f, 65537.f);
test_quad.VertexBottomLeft(-1.0f, -1.0f, 65537.f);
break;
test_quad.VertexTopLeft(-1.0f, 1.0f, 65537.f);
test_quad.VertexTopRight(1.0f, 1.0f, 65537.f);
test_quad.VertexBottomLeft(-1.0f, -1.0f, 65537.f);
break;
// TODO: One vertex with z < 0, depth clipping enabled, primitive gets properly (!) clipped
// TODO: One vertex with z < 0, depth clipping disabled, whole primitive gets drawn
}
// TODO: One vertex with z < 0, depth clipping enabled, primitive gets properly (!) clipped
// TODO: One vertex with z < 0, depth clipping disabled, whole primitive gets drawn
test_quad.Draw();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
}
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
if (expect_quad_to_be_drawn)
DO_TEST(result.r == 0xff, "Clipping test failed at step %d (expected quad to be shown at "
"pixel (%d, %d), but it was not)",
step, test_x, test_y);
else
DO_TEST(result.r == 0x00, "Clipping test failed at step %d (expected quad to be hidden at "
"pixel (%d, %d), but it was not)",
step, test_x, test_y);
test_quad.Draw();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
}
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
if (expect_quad_to_be_drawn)
DO_TEST(result.r == 0xff, "Clipping test failed at step %d (expected quad to be shown at pixel (%d, %d), but it was not)", step, test_x, test_y);
else
DO_TEST(result.r == 0x00, "Clipping test failed at step %d (expected quad to be hidden at pixel (%d, %d), but it was not)", step, test_x, test_y);
GXTest::DebugDisplayEfbContents();
}
END_TEST();
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GXTest::Init();
GXTest::Init();
ClipTest();
ClipTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -3,108 +3,110 @@
// Refer to the license.txt file included.
#include <initializer_list>
#include "common/hwtests.h"
#include <math.h>
#include <ogcsys.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
#include "gxtest/util.h"
#include <ogcsys.h>
void LightingTest()
{
START_TEST();
START_TEST();
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
LitChannel chan;
chan.hex = 0;
chan.matsource = 0; // from register
chan.ambsource = 0; // from register
chan.enablelighting = true;
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
LitChannel chan;
chan.hex = 0;
chan.matsource = 0; // from register
chan.ambsource = 0; // from register
chan.enablelighting = true;
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
CGX_LOAD_BP_REG(CGXDefault<TevStageCombiner::AlphaCombiner>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TevStageCombiner::AlphaCombiner>(0).hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
// Test to check how the hardware rounds the final computation of the
// lit color of a vertex. The formula is basically just
// (material color * lighting color), but the rounding isn't obvious
// because the hardware uses fixed-point math and takes some shortcuts.
for (int step = 0; step < 256*256; ++step)
{
int matcolor = step & 255;
int ambcolor = step >> 8;
// Test to check how the hardware rounds the final computation of the
// lit color of a vertex. The formula is basically just
// (material color * lighting color), but the rounding isn't obvious
// because the hardware uses fixed-point math and takes some shortcuts.
for (int step = 0; step < 256 * 256; ++step)
{
int matcolor = step & 255;
int ambcolor = step >> 8;
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
auto tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 0xff;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
auto tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 0xff;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
CGX_BEGIN_LOAD_XF_REGS(0x100a, 1);
wgPipe->U32 = (ambcolor << 24) | 255;
CGX_BEGIN_LOAD_XF_REGS(0x100a, 1);
wgPipe->U32 = (ambcolor << 24) | 255;
CGX_BEGIN_LOAD_XF_REGS(0x100c, 1);
wgPipe->U32 = (matcolor << 24) | 255;
CGX_BEGIN_LOAD_XF_REGS(0x100c, 1);
wgPipe->U32 = (matcolor << 24) | 255;
int test_x = 125, test_y = 25; // Somewhere within the viewport
int test_x = 125, test_y = 25; // Somewhere within the viewport
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
int expected = (matcolor * (ambcolor + (ambcolor >> 7))) >> 8;
DO_TEST(result.r == expected, "lighting test failed at amb %d mat %d actual %d", ambcolor, matcolor, result.r);
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
int expected = (matcolor * (ambcolor + (ambcolor >> 7))) >> 8;
DO_TEST(result.r == expected, "lighting test failed at amb %d mat %d actual %d", ambcolor,
matcolor, result.r);
GXTest::DebugDisplayEfbContents();
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break;
}
GXTest::DebugDisplayEfbContents();
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
break;
}
END_TEST();
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GXTest::Init();
GXTest::Init();
LightingTest();
LightingTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -3,134 +3,175 @@
// Refer to the license.txt file included.
#include <initializer_list>
#include "common/hwtests.h"
#include <math.h>
#include <ogcsys.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
#include "gxtest/util.h"
#include <ogcsys.h>
void CoordinatePrecisionTest()
{
START_TEST();
START_TEST();
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac.d = TEVALPHAARG_RASA;
CGX_LOAD_BP_REG(ac.hex);
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac.d = TEVALPHAARG_RASA;
CGX_LOAD_BP_REG(ac.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_RASC;
CGX_LOAD_BP_REG(cc.hex);
// Test at which coordinates a pixel is considered to be within a primitive.
// TODO: Not sure how to interpret the results, yet.
for (float xpos = 0.583328247070f; xpos <= 0.583328306675f; xpos = nextafterf(xpos,+1.0f))
{
CGX_SetViewport(xpos, 0.0f, 100.0f, 100.0f, 0.0f, 1.0f);
// Test at which coordinates a pixel is considered to be within a primitive.
// TODO: Not sure how to interpret the results, yet.
for (float xpos = 0.583328247070f; xpos <= 0.583328306675f; xpos = nextafterf(xpos, +1.0f))
{
CGX_SetViewport(xpos, 0.0f, 100.0f, 100.0f, 0.0f, 1.0f);
// first off, clear the full area.
GXTest::Quad().VertexTopLeft(-2.0, 2.0, 1.0).VertexBottomLeft(-2.0, -2.0, 1.0).VertexTopRight(2.0, 2.0, 1.0).VertexBottomRight(2.0, -2.0, 1.0).ColorRGBA(0,0,0,255).Draw();
GXTest::Quad().ColorRGBA(0,255,0,255).Draw();
// first off, clear the full area.
GXTest::Quad()
.VertexTopLeft(-2.0, 2.0, 1.0)
.VertexBottomLeft(-2.0, -2.0, 1.0)
.VertexTopRight(2.0, 2.0, 1.0)
.VertexBottomRight(2.0, -2.0, 1.0)
.ColorRGBA(0, 0, 0, 255)
.Draw();
GXTest::Quad().ColorRGBA(0, 255, 0, 255).Draw();
// now, draw the actual testing quad.
GXTest::Quad().VertexTopLeft(0, 1.0, 1.0).VertexBottomLeft(0, -1.0, 1.0).ColorRGBA(255,0,255,255).Draw();
GXTest::CopyToTestBuffer(50, 0, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
// now, draw the actual testing quad.
GXTest::Quad()
.VertexTopLeft(0, 1.0, 1.0)
.VertexBottomLeft(0, -1.0, 1.0)
.ColorRGBA(255, 0, 255, 255)
.Draw();
GXTest::CopyToTestBuffer(50, 0, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(0, 0, 128);
u8 expectation = (xpos <= 0.583328247070f) ? 255 : 0;
int subsample_index = (int)(xpos * 12.0f) % 12;
DO_TEST(result.r == expectation, "Incorrect rasterization (result=%d,expected=%d,screencoord=%.6f,subsample_index=%d)", result.r, expectation, xpos, subsample_index);
}
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(0, 0, 128);
u8 expectation = (xpos <= 0.583328247070f) ? 255 : 0;
int subsample_index = (int)(xpos * 12.0f) % 12;
DO_TEST(result.r == expectation,
"Incorrect rasterization (result=%d,expected=%d,screencoord=%.6f,subsample_index=%d)",
result.r, expectation, xpos, subsample_index);
}
// Test for the default pixel subsample location by creating tiny viewports (smaller than the pixel width)
// By drawing a quad over the whole viewport, we can check if the current viewport spans the subsample location.
// The results seem to indicate that the default subsample is located close to (but somewhat off) screen position 7/12.
// I (neobrain) am not sure if the sample indeed is not at that location or if it's just due to floating point rounding errors.
for (float xpos = 0.583297669888f; xpos <= 0.583328306675; xpos = nextafterf(xpos,+1.0f))
{
CGX_SetViewport(0.0, 0.0f, 100.0f, 100.0f, 0.0f, 1.0f);
// Test for the default pixel subsample location by creating tiny viewports (smaller than the
// pixel width)
// By drawing a quad over the whole viewport, we can check if the current viewport spans the
// subsample location.
// The results seem to indicate that the default subsample is located close to (but somewhat off)
// screen position 7/12.
// I (neobrain) am not sure if the sample indeed is not at that location or if it's just due to
// floating point rounding errors.
for (float xpos = 0.583297669888f; xpos <= 0.583328306675; xpos = nextafterf(xpos, +1.0f))
{
CGX_SetViewport(0.0, 0.0f, 100.0f, 100.0f, 0.0f, 1.0f);
// first off, clear the full area.
GXTest::Quad().VertexTopLeft(-2.0, 2.0, 1.0).VertexBottomLeft(-2.0, -2.0, 1.0).VertexTopRight(2.0, 2.0, 1.0).VertexBottomRight(2.0, -2.0, 1.0).ColorRGBA(0,0,0,255).Draw();
GXTest::Quad().ColorRGBA(0,255,0,255).Draw();
// first off, clear the full area.
GXTest::Quad()
.VertexTopLeft(-2.0, 2.0, 1.0)
.VertexBottomLeft(-2.0, -2.0, 1.0)
.VertexTopRight(2.0, 2.0, 1.0)
.VertexBottomRight(2.0, -2.0, 1.0)
.ColorRGBA(0, 0, 0, 255)
.Draw();
GXTest::Quad().ColorRGBA(0, 255, 0, 255).Draw();
// manual viewport setting to make sure we aren't limited (too much) by floating point precision
// 2.0e-5 seems to be the smallest possible viewport width which behaves sane.
// For this size, values of xpos from 0.583297729492 to 0.583328247070 will yield a covered pixel
float vp_width = 2.0e-5f;
CGX_BEGIN_LOAD_XF_REGS(0x101a,6);
wgPipe->F32 = vp_width;
wgPipe->F32 = -50.0f;
wgPipe->F32 = 16777215.0f;
wgPipe->F32 = 342.0f+xpos+vp_width;
wgPipe->F32 = 392.0f;
wgPipe->F32 = 16777215.0f;
// manual viewport setting to make sure we aren't limited (too much) by floating point precision
// 2.0e-5 seems to be the smallest possible viewport width which behaves sane.
// For this size, values of xpos from 0.583297729492 to 0.583328247070 will yield a covered
// pixel
float vp_width = 2.0e-5f;
CGX_BEGIN_LOAD_XF_REGS(0x101a, 6);
wgPipe->F32 = vp_width;
wgPipe->F32 = -50.0f;
wgPipe->F32 = 16777215.0f;
wgPipe->F32 = 342.0f + xpos + vp_width;
wgPipe->F32 = 392.0f;
wgPipe->F32 = 16777215.0f;
// now, draw the actual testing quad.
GXTest::Quad().ColorRGBA(255,0,255,255).Draw();
GXTest::CopyToTestBuffer(0, 0, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
// now, draw the actual testing quad.
GXTest::Quad().ColorRGBA(255, 0, 255, 255).Draw();
GXTest::CopyToTestBuffer(0, 0, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(0, 0, 128);
u8 expectation = (xpos == 0.583297669888f || xpos == 0.583328306675f) ? 0 : 255;
int subsample_index = (int)(xpos * 12.0f) % 12;
DO_TEST(result.r == expectation, "Incorrect rasterization (result=%d,expected=%d,screencoord=%.12f,subsample_index=%d)", result.r, expectation, xpos, subsample_index);
}
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(0, 0, 128);
u8 expectation = (xpos == 0.583297669888f || xpos == 0.583328306675f) ? 0 : 255;
int subsample_index = (int)(xpos * 12.0f) % 12;
DO_TEST(result.r == expectation,
"Incorrect rasterization (result=%d,expected=%d,screencoord=%.12f,subsample_index=%d)",
result.r, expectation, xpos, subsample_index);
}
// Guardband clipping indeed uses floating point math!
// Hence, the smallest floating point value smaller than -2.0 will yield a clipped primitive.
CGX_SetViewport(100.0f, 100.0f, 100.0f, 100.0f, 0.0f, 1.0f);
for (float xpos : {-2.0000000f, nextafterf(-2.0f, -1.0f)})
{
// first off, clear the full area, including the guardband
GXTest::Quad().VertexTopLeft(-2.0, 2.0, 1.0).VertexBottomLeft(-2.0, -2.0, 1.0).VertexTopRight(2.0, 2.0, 1.0).VertexBottomRight(2.0, -2.0, 1.0).ColorRGBA(0,0,0,255).Draw();
// Guardband clipping indeed uses floating point math!
// Hence, the smallest floating point value smaller than -2.0 will yield a clipped primitive.
CGX_SetViewport(100.0f, 100.0f, 100.0f, 100.0f, 0.0f, 1.0f);
for (float xpos : {-2.0000000f, nextafterf(-2.0f, -1.0f)})
{
// first off, clear the full area, including the guardband
GXTest::Quad()
.VertexTopLeft(-2.0, 2.0, 1.0)
.VertexBottomLeft(-2.0, -2.0, 1.0)
.VertexTopRight(2.0, 2.0, 1.0)
.VertexBottomRight(2.0, -2.0, 1.0)
.ColorRGBA(0, 0, 0, 255)
.Draw();
// now, draw the actual testing quad such that all vertices are outside the viewport (and on the same side of the viewport)
// The two left vertices are at the border of the guardband; if they are outside the guardband, the primitive gets clipped away.
GXTest::Quad().VertexTopLeft(xpos, 1.0, 1.0).VertexBottomLeft(xpos, -1.0, 1.0).VertexTopRight(xpos+1.0, 1.0, 1.0).VertexBottomRight(xpos+1.0, -1.0, 1.0).ColorRGBA(255,0,255,255).Draw();
GXTest::CopyToTestBuffer(50, 100, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
// now, draw the actual testing quad such that all vertices are outside the viewport (and on the
// same side of the viewport)
// The two left vertices are at the border of the guardband; if they are outside the guardband,
// the primitive gets clipped away.
GXTest::Quad()
.VertexTopLeft(xpos, 1.0, 1.0)
.VertexBottomLeft(xpos, -1.0, 1.0)
.VertexTopRight(xpos + 1.0, 1.0, 1.0)
.VertexBottomRight(xpos + 1.0, -1.0, 1.0)
.ColorRGBA(255, 0, 255, 255)
.Draw();
GXTest::CopyToTestBuffer(50, 100, 127, 127);
CGX_WaitForGpuToFinish();
GXTest::DebugDisplayEfbContents();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(10, 10, 128);
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(10, 10, 128);
int expectation = (xpos >= -2.0) ? 255 : 0;
DO_TEST(result.r == expectation, "Incorrect guardband clipping (result=%d,expected=%d,xpos=%.10f)", result.r, expectation, xpos);
};
int expectation = (xpos >= -2.0) ? 255 : 0;
DO_TEST(result.r == expectation,
"Incorrect guardband clipping (result=%d,expected=%d,xpos=%.10f)", result.r,
expectation, xpos);
};
END_TEST();
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GXTest::Init();
GXTest::Init();
CoordinatePrecisionTest();
CoordinatePrecisionTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -3,397 +3,398 @@
// Refer to the license.txt file included.
#include <initializer_list>
#include "common/hwtests.h"
#include <math.h>
#include <ogcsys.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wiiuse/wpad.h>
#include "common/hwtests.h"
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
#include "gxtest/util.h"
#include <ogcsys.h>
int TevCombinerExpectation(int a, int b, int c, int d, int shift, int bias, int op, int clamp)
{
a &= 255;
b &= 255;
c &= 255;
a &= 255;
b &= 255;
c &= 255;
// TODO: Does not handle compare mode, yet
c = c+(c>>7);
u16 lshift = (shift == 1) ? 1 : (shift == 2) ? 2 : 0;
u16 rshift = (shift == 3) ? 1 : 0;
int round_bias = (shift==3) ? 0 : ((op==1) ? 127 : 128);
int expected = (((a*(256-c) + b*c) << lshift)+round_bias)>>8; // lerp
expected = (d << lshift) + expected * ((op == 1) ? (-1) : 1);
expected += ((bias == 2) ? -128 : (bias == 1) ? 128 : 0) << lshift;
expected >>= rshift;
if (clamp)
expected = (expected < 0) ? 0 : (expected > 255) ? 255 : expected;
else
expected = (expected < -1024) ? -1024 : (expected > 1023) ? 1023 : expected;
return expected;
// TODO: Does not handle compare mode, yet
c = c + (c >> 7);
u16 lshift = (shift == 1) ? 1 : (shift == 2) ? 2 : 0;
u16 rshift = (shift == 3) ? 1 : 0;
int round_bias = (shift == 3) ? 0 : ((op == 1) ? 127 : 128);
int expected = (((a * (256 - c) + b * c) << lshift) + round_bias) >> 8; // lerp
expected = (d << lshift) + expected * ((op == 1) ? (-1) : 1);
expected += ((bias == 2) ? -128 : (bias == 1) ? 128 : 0) << lshift;
expected >>= rshift;
if (clamp)
expected = (expected < 0) ? 0 : (expected > 255) ? 255 : expected;
else
expected = (expected < -1024) ? -1024 : (expected > 1023) ? 1023 : expected;
return expected;
}
void TevCombinerTest()
{
START_TEST();
START_TEST();
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
LitChannel chan;
chan.hex = 0;
chan.matsource = 1; // from vertex
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
CGX_LOAD_BP_REG(ac.hex);
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
CGX_LOAD_BP_REG(ac.hex);
// Test if we can reliably extract all bits of the tev combiner output...
auto tevreg = CGXDefault<TevReg>(1, false); // c0
for (int i = 0; i < 2; ++i)
for (tevreg.red = -1024; tevreg.red != 1023; tevreg.red = tevreg.red+1)
{
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
// Test if we can reliably extract all bits of the tev combiner output...
auto tevreg = CGXDefault<TevReg>(1, false); // c0
for (int i = 0; i < 2; ++i)
for (tevreg.red = -1024; tevreg.red != 1023; tevreg.red = tevreg.red + 1)
{
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_C0;
CGX_LOAD_BP_REG(cc.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.d = TEVCOLORARG_C0;
CGX_LOAD_BP_REG(cc.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE<<24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
if (i == 0)
{
// 8 bits per channel: No worries about GetTevOutput making
// mistakes when writing to framebuffer or when performing
// an EFB copy.
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
if (i == 0)
{
// 8 bits per channel: No worries about GetTevOutput making
// mistakes when writing to framebuffer or when performing
// an EFB copy.
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
CGX_LOAD_BP_REG(ctrl.hex);
int result = GXTest::GetTevOutput(genmode, cc, ac).r;
int result = GXTest::GetTevOutput(genmode, cc, ac).r;
DO_TEST(result == tevreg.red, "Got %d, expected %d", result, (s32)tevreg.red);
}
else
{
// TODO: This doesn't quite work, yet.
break;
DO_TEST(result == tevreg.red, "Got %d, expected %d", result, (s32)tevreg.red);
}
else
{
// TODO: This doesn't quite work, yet.
break;
// 6 bits per channel: Implement GetTevOutput functionality
// manually, to verify how tev output is truncated to 6 bit
// and how EFB copies upscale that to 8 bit again.
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
CGX_LOAD_BP_REG(ctrl.hex);
// 6 bits per channel: Implement GetTevOutput functionality
// manually, to verify how tev output is truncated to 6 bit
// and how EFB copies upscale that to 8 bit again.
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
CGX_LOAD_BP_REG(ctrl.hex);
GXTest::Quad().AtDepth(1.0).ColorRGBA(255,255,255,255).Draw();
GXTest::CopyToTestBuffer(0, 0, 99, 9);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
u16 result = GXTest::ReadTestBuffer(5, 5, 100).r;
GXTest::Quad().AtDepth(1.0).ColorRGBA(255, 255, 255, 255).Draw();
GXTest::CopyToTestBuffer(0, 0, 99, 9);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
u16 result = GXTest::ReadTestBuffer(5, 5, 100).r;
int expected = (((tevreg.red+1)>>2)&0xFF) << 2;
expected = expected | (expected>>6);
DO_TEST(result == expected, "Run %d: Got %d, expected %d", (int)tevreg.red, result, expected);
}
}
int expected = (((tevreg.red + 1) >> 2) & 0xFF) << 2;
expected = expected | (expected >> 6);
DO_TEST(result == expected, "Run %d: Got %d, expected %d", (int)tevreg.red, result,
expected);
}
}
// Now: Randomized testing of tev combiners.
for (int i = 0x000000; i < 0x000F000; ++i)
{
if ((i & 0xFF00) == i)
network_printf("progress: %x\n", i);
// Now: Randomized testing of tev combiners.
for (int i = 0x000000; i < 0x000F000; ++i)
{
if ((i & 0xFF00) == i)
network_printf("progress: %x\n", i);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
// Randomly configured TEV stage, output in PREV.
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.a = TEVCOLORARG_C0;
cc.b = TEVCOLORARG_C1;
cc.c = TEVCOLORARG_C2;
cc.d = TEVCOLORARG_ZERO; // TEVCOLORARG_CPREV; // NOTE: TEVCOLORARG_CPREV doesn't actually seem to fetch its data from PREV when used in the first stage?
cc.shift = rand() % 4;
cc.bias = rand() % 3;
cc.op = rand()%2;
cc.clamp = rand() % 2;
CGX_LOAD_BP_REG(cc.hex);
// Randomly configured TEV stage, output in PREV.
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.a = TEVCOLORARG_C0;
cc.b = TEVCOLORARG_C1;
cc.c = TEVCOLORARG_C2;
cc.d = TEVCOLORARG_ZERO; // TEVCOLORARG_CPREV; // NOTE: TEVCOLORARG_CPREV doesn't actually seem
// to fetch its data from PREV when used in the first stage?
cc.shift = rand() % 4;
cc.bias = rand() % 3;
cc.op = rand() % 2;
cc.clamp = rand() % 2;
CGX_LOAD_BP_REG(cc.hex);
int a = -1024 + (rand() % 2048);
int b = -1024 + (rand() % 2048);
int c = -1024 + (rand() % 2048);
int d = 0; //-1024 + (rand() % 2048);
tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = a;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(2, false); // c1
tevreg.red = b;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(3, false); // c2
tevreg.red = c;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(0, false); // prev
tevreg.red = d;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
int a = -1024 + (rand() % 2048);
int b = -1024 + (rand() % 2048);
int c = -1024 + (rand() % 2048);
int d = 0; //-1024 + (rand() % 2048);
tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = a;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(2, false); // c1
tevreg.red = b;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(3, false); // c2
tevreg.red = c;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(0, false); // prev
tevreg.red = d;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE<<24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
int result = GXTest::GetTevOutput(genmode, cc, ac).r;
int result = GXTest::GetTevOutput(genmode, cc, ac).r;
int expected = TevCombinerExpectation(a, b, c, d, cc.shift, cc.bias, cc.op, cc.clamp);
DO_TEST(result == expected, "Mismatch on a=%d, b=%d, c=%d, d=%d, shift=%d, bias=%d, op=%d, clamp=%d: expected %d, got %d", a, b, c, d, (u32)cc.shift, (u32)cc.bias, (u32)cc.op, (u32)cc.clamp, expected, result);
int expected = TevCombinerExpectation(a, b, c, d, cc.shift, cc.bias, cc.op, cc.clamp);
DO_TEST(result == expected, "Mismatch on a=%d, b=%d, c=%d, d=%d, shift=%d, bias=%d, op=%d, "
"clamp=%d: expected %d, got %d",
a, b, c, d, (u32)cc.shift, (u32)cc.bias, (u32)cc.op, (u32)cc.clamp, expected, result);
WPAD_ScanPads();
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
break;
}
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)
break;
}
// Testing compare mode: (a.r > b.r) ? c.a : 0
// One of the following will be the case for the alpha combiner:
// (1) a.r will be assigned the value of c2.r (color combiner setting)
// (2) a.r will be assigned the value of c0.r (alpha combiner setting)
// If (1) is the case, the first run of this test will return black and
// the second one will return red. If (2) is the case, the test will
// always return black.
// Indeed, this test shows that the alpha combiner reads the a and b
// inputs from the color combiner setting, i.e. scenario (1) is
// accurate to hardware behavior.
for (int i = 0; i < 2; ++i)
{
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
// Testing compare mode: (a.r > b.r) ? c.a : 0
// One of the following will be the case for the alpha combiner:
// (1) a.r will be assigned the value of c2.r (color combiner setting)
// (2) a.r will be assigned the value of c0.r (alpha combiner setting)
// If (1) is the case, the first run of this test will return black and
// the second one will return red. If (2) is the case, the test will
// always return black.
// Indeed, this test shows that the alpha combiner reads the a and b
// inputs from the color combiner setting, i.e. scenario (1) is
// accurate to hardware behavior.
for (int i = 0; i < 2; ++i)
{
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.a = TEVCOLORARG_C2;
cc.b = TEVCOLORARG_C1;
CGX_LOAD_BP_REG(cc.hex);
auto cc = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc.a = TEVCOLORARG_C2;
cc.b = TEVCOLORARG_C1;
CGX_LOAD_BP_REG(cc.hex);
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac.bias = TevBias_COMPARE;
ac.a = TEVALPHAARG_A0; // different from color combiner
ac.b = TEVALPHAARG_A1; // same as color combiner
ac.c = TEVALPHAARG_A2;
CGX_LOAD_BP_REG(ac.hex);
auto ac = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac.bias = TevBias_COMPARE;
ac.a = TEVALPHAARG_A0; // different from color combiner
ac.b = TEVALPHAARG_A1; // same as color combiner
ac.c = TEVALPHAARG_A2;
CGX_LOAD_BP_REG(ac.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE<<24;
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 127; // 127 is always NOT less than 127.
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(1, false); // c0
tevreg.red = 127; // 127 is always NOT less than 127.
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(2, false); // c1
tevreg.red = 127;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(2, false); // c1
tevreg.red = 127;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(3, false); // c2
tevreg.red = 127+i; // 127+i is less than 127 iff i>0.
tevreg.alpha = 255;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
tevreg = CGXDefault<TevReg>(3, false); // c2
tevreg.red = 127 + i; // 127+i is less than 127 iff i>0.
tevreg.alpha = 255;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
int result = GXTest::GetTevOutput(genmode, cc, ac).a;
int expected = (i == 1) ? 255 : 0;
DO_TEST(result == expected, "Mismatch on run %d: expected %d, got %d", i, expected, result);
}
int result = GXTest::GetTevOutput(genmode, cc, ac).a;
int expected = (i == 1) ? 255 : 0;
DO_TEST(result == expected, "Mismatch on run %d: expected %d, got %d", i, expected, result);
}
END_TEST();
END_TEST();
}
void KonstTest()
{
START_TEST();
START_TEST();
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_LOAD_BP_REG(CGXDefault<TwoTevStageOrders>(0).hex);
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
CGX_BEGIN_LOAD_XF_REGS(0x1009, 1);
wgPipe->U32 = 1; // 1 color channel
LitChannel chan;
chan.hex = 0;
chan.matsource = 0; // from register
chan.ambsource = 0; // from register
chan.enablelighting = false;
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
LitChannel chan;
chan.hex = 0;
chan.matsource = 0; // from register
chan.ambsource = 0; // from register
chan.enablelighting = false;
CGX_BEGIN_LOAD_XF_REGS(0x100e, 1); // color channel 1
wgPipe->U32 = chan.hex;
CGX_BEGIN_LOAD_XF_REGS(0x1010, 1); // alpha channel 1
wgPipe->U32 = chan.hex;
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 1; // Two stages
CGX_LOAD_BP_REG(genmode.hex);
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 1; // Two stages
CGX_LOAD_BP_REG(genmode.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGB8_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
CGX_BEGIN_LOAD_XF_REGS(0x1005, 1);
wgPipe->U32 = 0; // 0 = enable clipping, 1 = disable clipping
// Set up "konst" colors with recognizable values.
for (int i = 0; i < 4; ++i)
{
auto tevreg = CGXDefault<TevReg>(i, true);
tevreg.red = 10 + 50 * i;
tevreg.green = 20 + 50 * i;
tevreg.blue = 30 + 50 * i;
tevreg.alpha = 40 + 50 * i;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
}
// Set up "konst" colors with recognizable values.
for (int i = 0; i < 4; ++i)
{
auto tevreg = CGXDefault<TevReg>(i, true);
tevreg.red = 10 + 50 * i;
tevreg.green = 20 + 50 * i;
tevreg.blue = 30 + 50 * i;
tevreg.alpha = 40 + 50 * i;
CGX_LOAD_BP_REG(tevreg.low);
CGX_LOAD_BP_REG(tevreg.high);
}
// Make sure blending is set appropriately (no blending).
BlendMode bm;
bm.hex = (BPMEM_BLENDMODE << 24);
bm.blendenable = 0;
bm.logicopenable = 0;
bm.dither = 1;
bm.colorupdate = 1;
bm.alphaupdate = 1;
bm.dstfactor = GX_BL_INVSRCALPHA;
bm.srcfactor = GX_BL_SRCALPHA;
bm.subtract = 0;
bm.logicmode = 0;
CGX_LOAD_BP_REG(bm.hex);
// Make sure blending is set appropriately (no blending).
BlendMode bm;
bm.hex = (BPMEM_BLENDMODE << 24);
bm.blendenable = 0;
bm.logicopenable = 0;
bm.dither = 1;
bm.colorupdate = 1;
bm.alphaupdate = 1;
bm.dstfactor = GX_BL_INVSRCALPHA;
bm.srcfactor = GX_BL_SRCALPHA;
bm.subtract = 0;
bm.logicmode = 0;
CGX_LOAD_BP_REG(bm.hex);
// Test for values returned for "konst" TEV inputs. Goes through
// all the possible values for kasel and kcsel.
for (int step = 0; step < 64; ++step)
{
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
// Test for values returned for "konst" TEV inputs. Goes through
// all the possible values for kasel and kcsel.
for (int step = 0; step < 64; ++step)
{
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
int test_x = 125, test_y = 25; // Somewhere within the viewport
int test_x = 125, test_y = 25; // Somewhere within the viewport
// First stage pulls in konst values.
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc0 = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc0.d = TEVCOLORARG_KONST;
CGX_LOAD_BP_REG(cc0.hex);
auto ac0 = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac0.d = TEVALPHAARG_KONST;
CGX_LOAD_BP_REG(ac0.hex);
// First stage pulls in konst values.
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc0 = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc0.d = TEVCOLORARG_KONST;
CGX_LOAD_BP_REG(cc0.hex);
auto ac0 = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac0.d = TEVALPHAARG_KONST;
CGX_LOAD_BP_REG(ac0.hex);
// Second stage makes sure the output is always in the red channel.
auto cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(1);
cc1.d = step < 32 ? TEVCOLORARG_CPREV : TEVCOLORARG_APREV;
CGX_LOAD_BP_REG(cc1.hex);
auto ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(1);
ac1.d = TEVALPHAARG_ZERO;
CGX_LOAD_BP_REG(ac1.hex);
// Second stage makes sure the output is always in the red channel.
auto cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(1);
cc1.d = step < 32 ? TEVCOLORARG_CPREV : TEVCOLORARG_APREV;
CGX_LOAD_BP_REG(cc1.hex);
auto ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(1);
ac1.d = TEVALPHAARG_ZERO;
CGX_LOAD_BP_REG(ac1.hex);
TevKSel sel;
sel.hex = BPMEM_TEV_KSEL << 24;
sel.swap1 = 0;
sel.swap2 = 0;
sel.kcsel0 = step < 32 ? step : 0;
sel.kcsel1 = 0;
sel.kasel0 = step >= 32 ? step : 0;
sel.kasel1 = 0;
CGX_LOAD_BP_REG(sel.hex);
TevKSel sel;
sel.hex = BPMEM_TEV_KSEL << 24;
sel.swap1 = 0;
sel.swap2 = 0;
sel.kcsel0 = step < 32 ? step : 0;
sel.kcsel1 = 0;
sel.kasel0 = step >= 32 ? step : 0;
sel.kasel1 = 0;
CGX_LOAD_BP_REG(sel.hex);
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
int expected[] = {
255, 223, 191, 159, 128, 96, 64, 32,
0, 0, 0, 0, 10, 60, 110, 160,
10, 60, 110, 160, 20, 70, 120, 170,
30, 80, 130, 180, 40, 90, 140, 190,
255, 223, 191, 159, 128, 96, 64, 32,
0, 0, 0, 0, 0, 0, 0, 0,
10, 60, 110, 160, 20, 70, 120, 170,
30, 80, 130, 180, 40, 90, 140, 190,
};
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
int expected[] = {
255, 223, 191, 159, 128, 96, 64, 32, 0, 0, 0, 0, 10, 60, 110, 160,
10, 60, 110, 160, 20, 70, 120, 170, 30, 80, 130, 180, 40, 90, 140, 190,
255, 223, 191, 159, 128, 96, 64, 32, 0, 0, 0, 0, 0, 0, 0, 0,
10, 60, 110, 160, 20, 70, 120, 170, 30, 80, 130, 180, 40, 90, 140, 190,
};
DO_TEST(expected[step] == result.r, "konst test failed; actual %d, expected %d", result.r, expected[step]);
DO_TEST(expected[step] == result.r, "konst test failed; actual %d, expected %d", result.r,
expected[step]);
GXTest::DebugDisplayEfbContents();
}
GXTest::DebugDisplayEfbContents();
}
// Test the behavior of TEVCOLORARG_HALF.
{
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
// Test the behavior of TEVCOLORARG_HALF.
{
auto genmode = CGXDefault<GenMode>();
genmode.numtevstages = 0; // One stage
CGX_LOAD_BP_REG(genmode.hex);
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
auto zmode = CGXDefault<ZMode>();
CGX_LOAD_BP_REG(zmode.hex);
int test_x = 125, test_y = 25; // Somewhere within the viewport
int test_x = 125, test_y = 25; // Somewhere within the viewport
// First stage pulls in konst values.
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc0 = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc0.d = TEVCOLORARG_HALF;
CGX_LOAD_BP_REG(cc0.hex);
auto ac0 = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac0.d = TEVALPHAARG_ZERO;
CGX_LOAD_BP_REG(ac0.hex);
// First stage pulls in konst values.
CGX_SetViewport(0.0f, 0.0f, 201.0f, 50.0f, 0.0f, 1.0f);
auto cc0 = CGXDefault<TevStageCombiner::ColorCombiner>(0);
cc0.d = TEVCOLORARG_HALF;
CGX_LOAD_BP_REG(cc0.hex);
auto ac0 = CGXDefault<TevStageCombiner::AlphaCombiner>(0);
ac0.d = TEVALPHAARG_ZERO;
CGX_LOAD_BP_REG(ac0.hex);
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
GXTest::Quad().ColorRGBA(0, 0, 0, 0xff).Draw();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::CopyToTestBuffer(0, 0, 199, 49);
CGX_WaitForGpuToFinish();
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
GXTest::Vec4<u8> result = GXTest::ReadTestBuffer(test_x, test_y, 200);
DO_TEST(result.r == 128, "TEVCOLORARG_HALF test failed; actual %d", result.r);
DO_TEST(result.r == 128, "TEVCOLORARG_HALF test failed; actual %d", result.r);
GXTest::DebugDisplayEfbContents();
}
GXTest::DebugDisplayEfbContents();
}
END_TEST();
END_TEST();
}
int main()
{
network_init();
WPAD_Init();
network_init();
WPAD_Init();
GXTest::Init();
GXTest::Init();
TevCombinerTest();
KonstTest();
TevCombinerTest();
KonstTest();
network_printf("Shutting down...\n");
network_shutdown();
network_printf("Shutting down...\n");
network_shutdown();
return 0;
return 0;
}

View File

@@ -3,10 +3,10 @@
// Refer to the license.txt file included.
#include <assert.h>
#include <string.h>
#include <malloc.h>
#include <gccore.h>
#include <malloc.h>
#include <ogc/video.h>
#include <string.h>
#include "gxtest/cgx.h"
#include "gxtest/cgx_defaults.h"
@@ -16,405 +16,403 @@
namespace GXTest
{
#define TEST_BUFFER_SIZE (640*528*4)
#define TEST_BUFFER_SIZE (640 * 528 * 4)
static u32* test_buffer;
#ifdef ENABLE_DEBUG_DISPLAY
static u32 fb = 0;
static void *frameBuffer[2] = { NULL, NULL};
static GXRModeObj *rmode;
static void* frameBuffer[2] = {NULL, NULL};
static GXRModeObj* rmode;
float yscale;
u32 xfbHeight;
#endif
void Init()
{
GXColor background = {0, 0x27, 0, 0xff};
GXColor background = {0, 0x27, 0, 0xff};
#if defined(ENABLE_DEBUG_DISPLAY)
VIDEO_Init();
VIDEO_Init();
rmode = VIDEO_GetPreferredMode(NULL);
rmode = VIDEO_GetPreferredMode(NULL);
frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if (rmode->viTVMode & VI_NON_INTERLACE)
VIDEO_WaitVSync();
CGX_Init();
CGX_Init();
GX_SetCopyClear(background, 0x00ffffff);
GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight);
xfbHeight = GX_SetDispCopyYScale(yscale);
GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, 1, rmode->vfilter);
GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight==2*rmode->xfbHeight)?1:0));
GX_SetDispCopyGamma(GX_GM_1_0);
GX_SetCopyClear(background, 0x00ffffff);
GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight);
xfbHeight = GX_SetDispCopyYScale(yscale);
GX_SetScissor(0, 0, rmode->fbWidth, rmode->efbHeight);
GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
GX_SetDispCopyDst(rmode->fbWidth, xfbHeight);
GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, 1, rmode->vfilter);
GX_SetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? 1 : 0));
GX_SetDispCopyGamma(GX_GM_1_0);
#else
CGX_Init();
CGX_Init();
GX_SetCopyClear(background, 0x00ffffff);
GX_SetViewport(0,0,640,528,0,1);
GX_SetScissor(0,0,640,528);
GX_SetCopyClear(background, 0x00ffffff);
GX_SetViewport(0, 0, 640, 528, 0, 1);
GX_SetScissor(0, 0, 640, 528);
#endif
test_buffer = (u32*)memalign(32, 640*528*4);
test_buffer = (u32*)memalign(32, 640 * 528 * 4);
GX_SetTexCopySrc(0, 0, 100, 100);
GX_SetTexCopyDst(100, 100, GX_TF_RGBA8, false);
GX_SetTexCopySrc(0, 0, 100, 100);
GX_SetTexCopyDst(100, 100, GX_TF_RGBA8, false);
// We draw a dummy quad here to apply cached GX state...
// TODO: Implement proper GPU initialization in GX so that we don't need
// to do this anymore
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
// We draw a dummy quad here to apply cached GX state...
// TODO: Implement proper GPU initialization in GX so that we don't need
// to do this anymore
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
Mtx model;
guMtxIdentity(model);
GX_LoadPosMtxImm(model, GX_PNMTX0);
Mtx model;
guMtxIdentity(model);
GX_LoadPosMtxImm(model, GX_PNMTX0);
float mtx[4][4];
memset(mtx, 0, sizeof(mtx));
mtx[0][0] = 1;
mtx[1][1] = 1;
mtx[2][2] = -1;
CGX_LoadProjectionMatrixOrthographic(mtx);
float mtx[4][4];
memset(mtx, 0, sizeof(mtx));
mtx[0][0] = 1;
mtx[1][1] = 1;
mtx[2][2] = -1;
CGX_LoadProjectionMatrixOrthographic(mtx);
GX_SetNumChans(1); // damnit dirty state...
GX_SetNumTexGens(0);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
GX_SetNumChans(1); // damnit dirty state...
GX_SetNumTexGens(0);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// Bottom right
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// Bottom right
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Top right
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Top right
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Top left
wgPipe->F32 = 1.0;
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Top left
wgPipe->F32 = 1.0;
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Bottom left
wgPipe->F32 = -1.0;
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
// Bottom left
wgPipe->F32 = -1.0;
wgPipe->F32 = -1.0;
wgPipe->F32 = 1.0;
wgPipe->U32 = 0xFFFFFFFF;
GX_End();
GX_Flush();
GX_End();
GX_Flush();
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE<<24;
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
PE_CONTROL ctrl;
ctrl.hex = BPMEM_ZCOMPARE << 24;
ctrl.pixel_format = PIXELFMT_RGBA6_Z24;
ctrl.zformat = ZC_LINEAR;
ctrl.early_ztest = 0;
CGX_LOAD_BP_REG(ctrl.hex);
}
void DebugDisplayEfbContents()
{
#ifdef ENABLE_DEBUG_DISPLAY
CGX_DoEfbCopyXfb(0, 0, rmode->fbWidth, rmode->efbHeight, xfbHeight, frameBuffer[fb]);
CGX_WaitForGpuToFinish();
CGX_DoEfbCopyXfb(0, 0, rmode->fbWidth, rmode->efbHeight, xfbHeight, frameBuffer[fb]);
CGX_WaitForGpuToFinish();
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
VIDEO_Flush();
VIDEO_WaitVSync();
fb ^= 1;
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
VIDEO_Flush();
VIDEO_WaitVSync();
fb ^= 1;
#endif
}
Vec4<u8> ReadTestBuffer(int s, int t, int width)
{
u16 sBlk = s >> 2;
u16 tBlk = t >> 2;
u16 widthBlks = (width >> 2) + 1;
u32 base = (tBlk * widthBlks + sBlk) << 5;
u16 blkS = s & 3;
u16 blkT = t & 3;
u32 blkOff = (blkT << 2) + blkS;
u16 sBlk = s >> 2;
u16 tBlk = t >> 2;
u16 widthBlks = (width >> 2) + 1;
u32 base = (tBlk * widthBlks + sBlk) << 5;
u16 blkS = s & 3;
u16 blkT = t & 3;
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1 ;
const u8* valAddr = ((u8*)test_buffer) + offset;
u32 offset = (base + blkOff) << 1;
const u8* valAddr = ((u8*)test_buffer) + offset;
Vec4<u8> ret;
ret.r = valAddr[1];
ret.g = valAddr[32];
ret.b = valAddr[33];
ret.a = valAddr[0];
return ret;
Vec4<u8> ret;
ret.r = valAddr[1];
ret.g = valAddr[32];
ret.b = valAddr[33];
ret.a = valAddr[0];
return ret;
}
Quad::Quad()
{
// top left
x[0] = -1.0;
y[0] = 1.0;
z[0] = 1.0;
// top left
x[0] = -1.0;
y[0] = 1.0;
z[0] = 1.0;
// top right
x[1] = 1.0;
y[1] = 1.0;
z[1] = 1.0;
// top right
x[1] = 1.0;
y[1] = 1.0;
z[1] = 1.0;
// bottom right
x[2] = 1.0;
y[2] = -1.0;
z[2] = 1.0;
// bottom right
x[2] = 1.0;
y[2] = -1.0;
z[2] = 1.0;
// bottom left
x[3] = -1.0;
y[3] = -1.0;
z[3] = 1.0;
// bottom left
x[3] = -1.0;
y[3] = -1.0;
z[3] = 1.0;
has_color = false;
has_color = false;
}
Quad& Quad::VertexTopLeft(f32 x, f32 y, f32 z)
{
this->x[0] = x;
this->y[0] = y;
this->z[0] = z;
return *this;
this->x[0] = x;
this->y[0] = y;
this->z[0] = z;
return *this;
}
Quad& Quad::VertexTopRight(f32 x, f32 y, f32 z)
{
this->x[1] = x;
this->y[1] = y;
this->z[1] = z;
return *this;
this->x[1] = x;
this->y[1] = y;
this->z[1] = z;
return *this;
}
Quad& Quad::VertexBottomRight(f32 x, f32 y, f32 z)
{
this->x[2] = x;
this->y[2] = y;
this->z[2] = z;
return *this;
this->x[2] = x;
this->y[2] = y;
this->z[2] = z;
return *this;
}
Quad& Quad::VertexBottomLeft(f32 x, f32 y, f32 z)
{
this->x[3] = x;
this->y[3] = y;
this->z[3] = z;
return *this;
this->x[3] = x;
this->y[3] = y;
this->z[3] = z;
return *this;
}
Quad& Quad::AtDepth(f32 depth)
{
z[0] = z[1] = z[2] = z[3] = depth;
z[0] = z[1] = z[2] = z[3] = depth;
return *this;
return *this;
}
Quad& Quad::ColorRGBA(u8 r, u8 g, u8 b, u8 a)
{
color = ((u32)r << 24) | ((u32)g << 16) | ((u32)b << 8) | (u32)a;
has_color = true;
color = ((u32)r << 24) | ((u32)g << 16) | ((u32)b << 8) | (u32)a;
has_color = true;
return *this;
return *this;
}
void Quad::Draw()
{
VAT vtxattr;
vtxattr.g0.Hex = 0;
vtxattr.g1.Hex = 0;
vtxattr.g2.Hex = 0;
VAT vtxattr;
vtxattr.g0.Hex = 0;
vtxattr.g1.Hex = 0;
vtxattr.g2.Hex = 0;
vtxattr.g0.PosElements = VA_TYPE_POS_XYZ;
vtxattr.g0.PosFormat = VA_FMT_F32;
vtxattr.g0.PosElements = VA_TYPE_POS_XYZ;
vtxattr.g0.PosFormat = VA_FMT_F32;
if (has_color)
{
vtxattr.g0.Color0Elements = VA_TYPE_CLR_RGBA;
vtxattr.g0.Color0Comp = VA_FMT_RGBA8;
}
if (has_color)
{
vtxattr.g0.Color0Elements = VA_TYPE_CLR_RGBA;
vtxattr.g0.Color0Comp = VA_FMT_RGBA8;
}
// TODO: Figure out what this does and why it needs to be 1 for Dolphin not to error out
vtxattr.g0.ByteDequant = 1;
// TODO: Figure out what this does and why it needs to be 1 for Dolphin not to error out
vtxattr.g0.ByteDequant = 1;
TVtxDesc vtxdesc;
vtxdesc.Hex = 0;
vtxdesc.Position = VTXATTR_DIRECT;
TVtxDesc vtxdesc;
vtxdesc.Hex = 0;
vtxdesc.Position = VTXATTR_DIRECT;
if (has_color)
vtxdesc.Color0 = VTXATTR_DIRECT;
if (has_color)
vtxdesc.Color0 = VTXATTR_DIRECT;
// TODO: Not sure if the order of these two is correct
CGX_LOAD_CP_REG(0x50, vtxdesc.Hex0);
CGX_LOAD_CP_REG(0x60, vtxdesc.Hex1);
// TODO: Not sure if the order of these two is correct
CGX_LOAD_CP_REG(0x50, vtxdesc.Hex0);
CGX_LOAD_CP_REG(0x60, vtxdesc.Hex1);
CGX_LOAD_CP_REG(0x70, vtxattr.g0.Hex);
CGX_LOAD_CP_REG(0x80, vtxattr.g1.Hex);
CGX_LOAD_CP_REG(0x90, vtxattr.g2.Hex);
CGX_LOAD_CP_REG(0x70, vtxattr.g0.Hex);
CGX_LOAD_CP_REG(0x80, vtxattr.g1.Hex);
CGX_LOAD_CP_REG(0x90, vtxattr.g2.Hex);
/* TODO: Should reset this matrix..
float mtx[3][4];
memset(&mtx, 0, sizeof(mtx));
mtx[0][0] = 1.0;
mtx[1][1] = 1.0;
mtx[2][2] = 1.0;
CGX_LoadPosMatrixDirect(mtx, 0);*/
/* TODO: Should reset this matrix..
float mtx[3][4];
memset(&mtx, 0, sizeof(mtx));
mtx[0][0] = 1.0;
mtx[1][1] = 1.0;
mtx[2][2] = 1.0;
CGX_LoadPosMatrixDirect(mtx, 0);*/
float mtx[4][4];
memset(mtx, 0, sizeof(mtx));
mtx[0][0] = 1;
mtx[1][1] = 1;
mtx[2][2] = -1;
CGX_LoadProjectionMatrixOrthographic(mtx);
float mtx[4][4];
memset(mtx, 0, sizeof(mtx));
mtx[0][0] = 1;
mtx[1][1] = 1;
mtx[2][2] = -1;
CGX_LoadProjectionMatrixOrthographic(mtx);
wgPipe->U8 = 0x80; // draw quads
wgPipe->U16 = 4; // 4 vertices
wgPipe->U8 = 0x80; // draw quads
wgPipe->U16 = 4; // 4 vertices
for (int i = 0; i < 4; ++i)
{
wgPipe->F32 = x[i];
wgPipe->F32 = y[i];
wgPipe->F32 = z[i];
for (int i = 0; i < 4; ++i)
{
wgPipe->F32 = x[i];
wgPipe->F32 = y[i];
wgPipe->F32 = z[i];
if (has_color)
wgPipe->U32 = color;
}
if (has_color)
wgPipe->U32 = color;
}
}
void CopyToTestBuffer(int left_most_pixel, int top_most_pixel, int right_most_pixel, int bottom_most_pixel)
void CopyToTestBuffer(int left_most_pixel, int top_most_pixel, int right_most_pixel,
int bottom_most_pixel)
{
// TODO: Do we need to impose additional constraints on the parameters?
memset(test_buffer, 0, TEST_BUFFER_SIZE);
CGX_DoEfbCopyTex(left_most_pixel, top_most_pixel,
right_most_pixel - left_most_pixel + 1,
bottom_most_pixel - top_most_pixel + 1, 0x6 /*RGBA8*/,
false, test_buffer);
// TODO: Do we need to impose additional constraints on the parameters?
memset(test_buffer, 0, TEST_BUFFER_SIZE);
CGX_DoEfbCopyTex(left_most_pixel, top_most_pixel, right_most_pixel - left_most_pixel + 1,
bottom_most_pixel - top_most_pixel + 1, 0x6 /*RGBA8*/, false, test_buffer);
}
Vec4<int> GetTevOutput(const GenMode& genmode, const TevStageCombiner::ColorCombiner& last_cc, const TevStageCombiner::AlphaCombiner& last_ac)
Vec4<int> GetTevOutput(const GenMode& genmode, const TevStageCombiner::ColorCombiner& last_cc,
const TevStageCombiner::AlphaCombiner& last_ac)
{
int previous_stage = ((last_cc.hex >> 24)-BPMEM_TEV_COLOR_ENV)>>1;
assert(previous_stage < 13);
assert(previous_stage == (((last_ac.hex >> 24)-BPMEM_TEV_ALPHA_ENV)>>1));
int previous_stage = ((last_cc.hex >> 24) - BPMEM_TEV_COLOR_ENV) >> 1;
assert(previous_stage < 13);
assert(previous_stage == (((last_ac.hex >> 24) - BPMEM_TEV_ALPHA_ENV) >> 1));
// The TEV output gets truncated to 8 bits when writing to the EFB.
// Hence, we cannot retrieve all 11 TEV output bits directly.
// Instead, we're performing two render passes, one of which retrieves
// the lower 6 output bits, the other one of which retrieves the upper
// 5 bits.
// The TEV output gets truncated to 8 bits when writing to the EFB.
// Hence, we cannot retrieve all 11 TEV output bits directly.
// Instead, we're performing two render passes, one of which retrieves
// the lower 6 output bits, the other one of which retrieves the upper
// 5 bits.
// FIRST RENDER PASS:
// As set up by the caller, with one additional tev stage multiplying the result by 4.
// This will retrieve the lower 6 bits of the TEV output.
// FIRST RENDER PASS:
// As set up by the caller, with one additional tev stage multiplying the result by 4.
// This will retrieve the lower 6 bits of the TEV output.
auto gm = genmode;
gm.numtevstages = previous_stage + 1; // one additional stage
CGX_LOAD_BP_REG(gm.hex);
auto gm = genmode;
gm.numtevstages = previous_stage + 1; // one additional stage
CGX_LOAD_BP_REG(gm.hex);
// Enable new TEV stage. Note that we are using the "a" input here to make
// sure the input doesn't get erroneously clamped to 11 bit range.
auto cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage+1);
cc1.a = last_cc.dest * 2;
cc1.shift = TEVSCALE_4;
CGX_LOAD_BP_REG(cc1.hex);
// Enable new TEV stage. Note that we are using the "a" input here to make
// sure the input doesn't get erroneously clamped to 11 bit range.
auto cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage + 1);
cc1.a = last_cc.dest * 2;
cc1.shift = TEVSCALE_4;
CGX_LOAD_BP_REG(cc1.hex);
auto ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage+1);
ac1.a = last_ac.dest * 2;
ac1.shift = TEVSCALE_4;
CGX_LOAD_BP_REG(ac1.hex);
auto ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage + 1);
ac1.a = last_ac.dest * 2;
ac1.shift = TEVSCALE_4;
CGX_LOAD_BP_REG(ac1.hex);
memset(test_buffer, 0, TEST_BUFFER_SIZE); // Just for debugging
Quad().AtDepth(1.0).ColorRGBA(255,255,255,255).Draw();
CGX_DoEfbCopyTex(0, 0, 100, 100, 0x6 /*RGBA8*/, false, test_buffer);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
u16 result1r = ReadTestBuffer(5, 5, 100).r >> 2;
u16 result1g = ReadTestBuffer(5, 5, 100).g >> 2;
u16 result1b = ReadTestBuffer(5, 5, 100).b >> 2;
u16 result1a = ReadTestBuffer(5, 5, 100).a >> 2;
memset(test_buffer, 0, TEST_BUFFER_SIZE); // Just for debugging
Quad().AtDepth(1.0).ColorRGBA(255, 255, 255, 255).Draw();
CGX_DoEfbCopyTex(0, 0, 100, 100, 0x6 /*RGBA8*/, false, test_buffer);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
u16 result1r = ReadTestBuffer(5, 5, 100).r >> 2;
u16 result1g = ReadTestBuffer(5, 5, 100).g >> 2;
u16 result1b = ReadTestBuffer(5, 5, 100).b >> 2;
u16 result1a = ReadTestBuffer(5, 5, 100).a >> 2;
// SECOND RENDER PASS
// Uses three additional TEV stages which shift the previous result
// three bits to the right. This is necessary to read off the 5 upper bits,
// 3 of which got masked off when writing to the EFB in the first pass.
gm = genmode;
gm.numtevstages = previous_stage + 3; // three additional stages
CGX_LOAD_BP_REG(gm.hex);
// SECOND RENDER PASS
// Uses three additional TEV stages which shift the previous result
// three bits to the right. This is necessary to read off the 5 upper bits,
// 3 of which got masked off when writing to the EFB in the first pass.
gm = genmode;
gm.numtevstages = previous_stage + 3; // three additional stages
CGX_LOAD_BP_REG(gm.hex);
// The following tev stages are exclusively used to rightshift the
// upper bits such that they get written to the render target.
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage+1);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
// The following tev stages are exclusively used to rightshift the
// upper bits such that they get written to the render target.
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage + 1);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage+1);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage + 1);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage+2);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage + 2);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage+2);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage + 2);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage+3);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
cc1 = CGXDefault<TevStageCombiner::ColorCombiner>(previous_stage + 3);
cc1.d = last_cc.dest * 2;
cc1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(cc1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage+3);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
ac1 = CGXDefault<TevStageCombiner::AlphaCombiner>(previous_stage + 3);
ac1.d = last_ac.dest * 2;
ac1.shift = TEVDIVIDE_2;
CGX_LOAD_BP_REG(ac1.hex);
memset(test_buffer, 0, TEST_BUFFER_SIZE);
Quad().AtDepth(1.0).ColorRGBA(255,255,255,255).Draw();
CGX_DoEfbCopyTex(0, 0, 100, 100, 0x6 /*RGBA8*/, false, test_buffer);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
memset(test_buffer, 0, TEST_BUFFER_SIZE);
Quad().AtDepth(1.0).ColorRGBA(255, 255, 255, 255).Draw();
CGX_DoEfbCopyTex(0, 0, 100, 100, 0x6 /*RGBA8*/, false, test_buffer);
CGX_ForcePipelineFlush();
CGX_WaitForGpuToFinish();
u16 result2r = ReadTestBuffer(5, 5, 100).r >> 3;
u16 result2g = ReadTestBuffer(5, 5, 100).g >> 3;
u16 result2b = ReadTestBuffer(5, 5, 100).b >> 3;
u16 result2a = ReadTestBuffer(5, 5, 100).a >> 3;
u16 result2r = ReadTestBuffer(5, 5, 100).r >> 3;
u16 result2g = ReadTestBuffer(5, 5, 100).g >> 3;
u16 result2b = ReadTestBuffer(5, 5, 100).b >> 3;
u16 result2a = ReadTestBuffer(5, 5, 100).a >> 3;
// uh.. let's just say this works, but I guess it could be simplified.
Vec4<int> result;
result.r = result1r + ((result2r & 0x10) ? (-0x400+((result2r&0xF)<<6)) : (result2r<<6));
result.g = result1g + ((result2g & 0x10) ? (-0x400+((result2g&0xF)<<6)) : (result2g<<6));
result.b = result1b + ((result2b & 0x10) ? (-0x400+((result2b&0xF)<<6)) : (result2b<<6));
result.a = result1a + ((result2a & 0x10) ? (-0x400+((result2a&0xF)<<6)) : (result2a<<6));
return result;
// uh.. let's just say this works, but I guess it could be simplified.
Vec4<int> result;
result.r = result1r + ((result2r & 0x10) ? (-0x400 + ((result2r & 0xF) << 6)) : (result2r << 6));
result.g = result1g + ((result2g & 0x10) ? (-0x400 + ((result2g & 0xF) << 6)) : (result2g << 6));
result.b = result1b + ((result2b & 0x10) ? (-0x400 + ((result2b & 0xF) << 6)) : (result2b << 6));
result.a = result1a + ((result2a & 0x10) ? (-0x400 + ((result2a & 0xF) << 6)) : (result2a << 6));
return result;
}
}

View File

@@ -6,43 +6,42 @@
namespace GXTest
{
// Four component vector with arbitrary base type
template<typename T>
template <typename T>
union Vec4
{
struct
{
T r, g, b, a;
};
struct
{
T x, y, z, w;
};
struct
{
T r, g, b, a;
};
struct
{
T x, y, z, w;
};
};
// Utility class to draw quads
class Quad
{
public:
Quad();
Quad();
Quad& VertexTopLeft(f32 x, f32 y, f32 z);
Quad& VertexTopRight(f32 x, f32 y, f32 z);
Quad& VertexBottomRight(f32 x, f32 y, f32 z);
Quad& VertexBottomLeft(f32 x, f32 y, f32 z);
Quad& VertexTopLeft(f32 x, f32 y, f32 z);
Quad& VertexTopRight(f32 x, f32 y, f32 z);
Quad& VertexBottomRight(f32 x, f32 y, f32 z);
Quad& VertexBottomLeft(f32 x, f32 y, f32 z);
Quad& AtDepth(f32 depth);
Quad& AtDepth(f32 depth);
Quad& ColorRGBA(u8 r, u8 g, u8 b, u8 a);
Quad& ColorRGBA(u8 r, u8 g, u8 b, u8 a);
void Draw();
void Draw();
private:
f32 x[4], y[4], z[4];
f32 x[4], y[4], z[4];
bool has_color;
u32 color;
bool has_color;
u32 color;
};
// Initialize CGX and GXTest
@@ -53,7 +52,8 @@ void Init();
void DrawFullScreenQuad();
// Perform an RGBA8 EFB copy to the internal testing buffer
void CopyToTestBuffer(int left_most_pixel, int top_most_pixel, int right_most_pixel, int bottom_most_pixel);
void CopyToTestBuffer(int left_most_pixel, int top_most_pixel, int right_most_pixel,
int bottom_most_pixel);
// Read back result from test buffer
// CopyToTestBuffer needs to be called before using this.
@@ -65,9 +65,9 @@ Vec4<u8> ReadTestBuffer(int x, int y, int previous_copy_width);
// calling this function. The function logic adds 3 additional tev stages,
// so care must be taken not to enable more than 13 tev stages before usage.
// NOTE: This will only work correctly if the EFB format is set to RGB8
Vec4<int> GetTevOutput(const GenMode& genmode, const TevStageCombiner::ColorCombiner& last_cc, const TevStageCombiner::AlphaCombiner& last_ac);
Vec4<int> GetTevOutput(const GenMode& genmode, const TevStageCombiner::ColorCombiner& last_cc,
const TevStageCombiner::AlphaCombiner& last_ac);
void DebugDisplayEfbContents();
} // namespace
} // namespace