mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-22 06:42:59 +12:00
Merge branch 'master' of github.com:fleroviux/Panda3DS into pica-tev-emulation
This commit is contained in:
commit
4cb7e3625b
13 changed files with 427 additions and 412 deletions
|
@ -47,7 +47,7 @@ class GPU {
|
||||||
};
|
};
|
||||||
|
|
||||||
u64 getVertexShaderInputConfig() {
|
u64 getVertexShaderInputConfig() {
|
||||||
return u64(regs[PICAInternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICAInternalRegs::VertexShaderInputCfgHigh]) << 32);
|
return u64(regs[PICA::InternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICA::InternalRegs::VertexShaderInputCfgHigh]) << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<AttribInfo, maxAttribCount> attributeInfo; // Info for each of the 12 attributes
|
std::array<AttribInfo, maxAttribCount> attributeInfo; // Info for each of the 12 attributes
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
namespace PICAInternalRegs {
|
namespace PICA {
|
||||||
|
namespace InternalRegs {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
// Rasterizer registers
|
// Rasterizer registers
|
||||||
ViewportWidth = 0x41,
|
ViewportWidth = 0x41,
|
||||||
|
@ -128,4 +129,96 @@ namespace PICAInternalRegs {
|
||||||
VertexShaderOpDescriptorData6 = 0x2DC,
|
VertexShaderOpDescriptorData6 = 0x2DC,
|
||||||
VertexShaderOpDescriptorData7 = 0x2DD,
|
VertexShaderOpDescriptorData7 = 0x2DD,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TextureFmt : u32 {
|
||||||
|
RGBA8 = 0x0,
|
||||||
|
RGB8 = 0x1,
|
||||||
|
RGBA5551 = 0x2,
|
||||||
|
RGB565 = 0x3,
|
||||||
|
RGBA4 = 0x4,
|
||||||
|
IA8 = 0x5,
|
||||||
|
RG8 = 0x6,
|
||||||
|
I8 = 0x7,
|
||||||
|
A8 = 0x8,
|
||||||
|
IA4 = 0x9,
|
||||||
|
I4 = 0xA,
|
||||||
|
A4 = 0xB,
|
||||||
|
ETC1 = 0xC,
|
||||||
|
ETC1A4 = 0xD,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ColorFmt : u32 {
|
||||||
|
RGBA8 = 0x0,
|
||||||
|
RGB8 = 0x1,
|
||||||
|
RGBA5551 = 0x2,
|
||||||
|
RGB565 = 0x3,
|
||||||
|
RGBA4 = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DepthFmt : u32 {
|
||||||
|
Depth16 = 0,
|
||||||
|
Unknown1 = 1, // Technically selectable, but function is unknown
|
||||||
|
Depth24 = 2,
|
||||||
|
Depth24Stencil8 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the string representation of a texture format
|
||||||
|
inline constexpr const char* textureFormatToString(TextureFmt fmt) {
|
||||||
|
switch (fmt) {
|
||||||
|
case TextureFmt::RGBA8: return "RGBA8";
|
||||||
|
case TextureFmt::RGB8: return "RGB8";
|
||||||
|
case TextureFmt::RGBA5551: return "RGBA5551";
|
||||||
|
case TextureFmt::RGB565: return "RGB565";
|
||||||
|
case TextureFmt::RGBA4: return "RGBA4";
|
||||||
|
case TextureFmt::IA8: return "IA8";
|
||||||
|
case TextureFmt::RG8: return "RG8";
|
||||||
|
case TextureFmt::I8: return "I8";
|
||||||
|
case TextureFmt::A8: return "A8";
|
||||||
|
case TextureFmt::IA4: return "IA4";
|
||||||
|
case TextureFmt::I4: return "I4";
|
||||||
|
case TextureFmt::A4: return "A4";
|
||||||
|
case TextureFmt::ETC1: return "ETC1";
|
||||||
|
case TextureFmt::ETC1A4: return "ETC1A4";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr const char* textureFormatToString(ColorFmt fmt) {
|
||||||
|
return textureFormatToString(static_cast<TextureFmt>(fmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool hasStencil(DepthFmt format) { return format == PICA::DepthFmt::Depth24Stencil8; }
|
||||||
|
|
||||||
|
// Size occupied by each pixel in bytes
|
||||||
|
|
||||||
|
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
|
||||||
|
inline constexpr usize sizePerPixel(TextureFmt format) {
|
||||||
|
switch (format) {
|
||||||
|
case TextureFmt::RGB8: return 3;
|
||||||
|
case TextureFmt::RGBA8: return 4;
|
||||||
|
default: return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr usize sizePerPixel(ColorFmt format) {
|
||||||
|
return sizePerPixel(static_cast<TextureFmt>(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr usize sizePerPixel(DepthFmt format) {
|
||||||
|
switch (format) {
|
||||||
|
case DepthFmt::Depth16: return 2;
|
||||||
|
case DepthFmt::Depth24: return 3;
|
||||||
|
case DepthFmt::Depth24Stencil8: return 4;
|
||||||
|
default: return 1; // Invalid format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PrimType : u32 {
|
||||||
|
TriangleList = 0,
|
||||||
|
TriangleStrip = 1,
|
||||||
|
TriangleFan = 2,
|
||||||
|
GeometryPrimitive = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace PICA
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdarg>
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -8,8 +8,16 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "termcolor.hpp"
|
#include "termcolor.hpp"
|
||||||
|
|
||||||
|
// We have to detect and special-case AppleClang at the moment since its C++20 support is finicky and doesn't quite support std::bit_cast
|
||||||
|
#if defined(__clang__) && defined(__apple_build_version__)
|
||||||
|
#define HELPERS_APPLE_CLANG
|
||||||
|
#else
|
||||||
|
#include <bit>
|
||||||
|
#endif
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using u16 = std::uint16_t;
|
using u16 = std::uint16_t;
|
||||||
using u32 = std::uint32_t;
|
using u32 = std::uint32_t;
|
||||||
|
@ -27,7 +35,7 @@ namespace Helpers {
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
std::cout << termcolor::on_red << "[FATAL] ";
|
std::cout << termcolor::on_red << "[FATAL] ";
|
||||||
std::vprintf (fmt, args);
|
std::vprintf(fmt, args);
|
||||||
std::cout << termcolor::reset << "\n";
|
std::cout << termcolor::reset << "\n";
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
@ -38,63 +46,59 @@ namespace Helpers {
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
std::cout << termcolor::on_red << "[Warning] ";
|
std::cout << termcolor::on_red << "[Warning] ";
|
||||||
std::vprintf (fmt, args);
|
std::vprintf(fmt, args);
|
||||||
std::cout << termcolor::reset << "\n";
|
std::cout << termcolor::reset << "\n";
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector <u8> loadROM(std::string directory) {
|
static std::vector<u8> loadROM(std::string directory) {
|
||||||
std::ifstream file (directory, std::ios::binary);
|
std::ifstream file(directory, std::ios::binary);
|
||||||
if (file.fail())
|
if (file.fail()) panic("Couldn't read %s", directory.c_str());
|
||||||
panic("Couldn't read %s", directory.c_str());
|
|
||||||
|
|
||||||
std::vector<u8> ROM;
|
std::vector<u8> ROM;
|
||||||
|
|
||||||
file.unsetf(std::ios::skipws);
|
file.unsetf(std::ios::skipws);
|
||||||
ROM.insert(ROM.begin(),
|
ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
|
||||||
std::istream_iterator<uint8_t>(file),
|
|
||||||
std::istream_iterator<uint8_t>());
|
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
printf ("%s loaded successfully\n", directory.c_str());
|
printf("%s loaded successfully\n", directory.c_str());
|
||||||
return ROM;
|
return ROM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool buildingInDebugMode() {
|
static constexpr bool buildingInDebugMode() {
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void debug_printf (const char* fmt, ...) {
|
static void debug_printf(const char* fmt, ...) {
|
||||||
if constexpr (buildingInDebugMode()) {
|
if constexpr (buildingInDebugMode()) {
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
std::vprintf (fmt, args);
|
std::vprintf(fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign extend an arbitrary-size value to 32 bits
|
/// Sign extend an arbitrary-size value to 32 bits
|
||||||
static constexpr u32 inline signExtend32 (u32 value, u32 startingSize) {
|
static constexpr u32 inline signExtend32(u32 value, u32 startingSize) {
|
||||||
auto temp = (s32) value;
|
auto temp = (s32)value;
|
||||||
auto bitsToShift = 32 - startingSize;
|
auto bitsToShift = 32 - startingSize;
|
||||||
return (u32) (temp << bitsToShift >> bitsToShift);
|
return (u32)(temp << bitsToShift >> bitsToShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign extend an arbitrary-size value to 16 bits
|
/// Sign extend an arbitrary-size value to 16 bits
|
||||||
static constexpr u16 signExtend16 (u16 value, u32 startingSize) {
|
static constexpr u16 signExtend16(u16 value, u32 startingSize) {
|
||||||
auto temp = (s16) value;
|
auto temp = (s16)value;
|
||||||
auto bitsToShift = 16 - startingSize;
|
auto bitsToShift = 16 - startingSize;
|
||||||
return (u16) (temp << bitsToShift >> bitsToShift);
|
return (u16)(temp << bitsToShift >> bitsToShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mask with `count` number of one bits.
|
/// Create a mask with `count` number of one bits.
|
||||||
template<typename T, usize count>
|
template <typename T, usize count>
|
||||||
static constexpr T ones () {
|
static constexpr T ones() {
|
||||||
constexpr usize bitsize = CHAR_BIT * sizeof(T);
|
constexpr usize bitsize = CHAR_BIT * sizeof(T);
|
||||||
static_assert(count <= bitsize, "count larger than bitsize of T");
|
static_assert(count <= bitsize, "count larger than bitsize of T");
|
||||||
|
|
||||||
|
@ -105,25 +109,23 @@ namespace Helpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract bits from an integer-type
|
/// Extract bits from an integer-type
|
||||||
template<usize offset, typename T>
|
template <usize offset, typename T>
|
||||||
static constexpr T getBit (T value) {
|
static constexpr T getBit(T value) {
|
||||||
return (value >> offset) & T(1);
|
return (value >> offset) & T(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract bits from an integer-type
|
/// Extract bits from an integer-type
|
||||||
template<usize offset, usize bits, typename T>
|
template <usize offset, usize bits, typename T>
|
||||||
static constexpr T getBits (T value) {
|
static constexpr T getBits(T value) {
|
||||||
return (value >> offset) & ones<T, bits>();
|
return (value >> offset) & ones<T, bits>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a bit "bit" of value is set
|
/// Check if a bit "bit" of value is set
|
||||||
static constexpr bool isBitSet (u32 value, int bit) {
|
static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; }
|
||||||
return (value >> bit) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// rotate number right
|
/// rotate number right
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr T rotr (T value, int bits) {
|
static constexpr T rotr(T value, int bits) {
|
||||||
constexpr auto bitWidth = sizeof(T) * 8;
|
constexpr auto bitWidth = sizeof(T) * 8;
|
||||||
bits &= bitWidth - 1;
|
bits &= bitWidth - 1;
|
||||||
return (value >> bits) | (value << (bitWidth - bits));
|
return (value >> bits) | (value << (bitWidth - bits));
|
||||||
|
@ -131,48 +133,50 @@ namespace Helpers {
|
||||||
|
|
||||||
// rotate number left
|
// rotate number left
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr T rotl (T value, int bits) {
|
static constexpr T rotl(T value, int bits) {
|
||||||
constexpr auto bitWidth = sizeof(T) * 8;
|
constexpr auto bitWidth = sizeof(T) * 8;
|
||||||
bits &= bitWidth - 1;
|
bits &= bitWidth - 1;
|
||||||
return (value << bits) | (value >> (bitWidth - bits));
|
return (value << bits) | (value >> (bitWidth - bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
||||||
template <typename T, T Begin, class Func, T ...Is>
|
template <typename T, T Begin, class Func, T... Is>
|
||||||
static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
|
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
|
||||||
( f( std::integral_constant<T, Begin + Is>{ } ),... );
|
(f(std::integral_constant<T, Begin + Is>{}), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, T Begin, T End, class Func>
|
template <typename T, T Begin, T End, class Func>
|
||||||
static constexpr void static_for(Func&& f) {
|
static constexpr void static_for(Func&& f) {
|
||||||
static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
|
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For values < 0x99
|
// For values < 0x99
|
||||||
static constexpr inline u8 incBCDByte(u8 value) {
|
static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; }
|
||||||
return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
|
|
||||||
|
#ifdef HELPERS_APPLE_CLANG
|
||||||
|
template <class To, class From>
|
||||||
|
constexpr To bit_cast(const From& from) noexcept {
|
||||||
|
return *reinterpret_cast<const To*>(&from);
|
||||||
}
|
}
|
||||||
};
|
#else
|
||||||
|
template <class To, class From>
|
||||||
|
constexpr To bit_cast(const From& from) noexcept {
|
||||||
|
return std::bit_cast<To, From>(from);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}; // namespace Helpers
|
||||||
|
|
||||||
// UDLs for memory size values
|
// UDLs for memory size values
|
||||||
constexpr size_t operator""_KB(unsigned long long int x) {
|
constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; }
|
||||||
return 1024ULL * x;
|
constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; }
|
||||||
}
|
constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; }
|
||||||
|
|
||||||
constexpr size_t operator""_MB(unsigned long long int x) {
|
|
||||||
return 1024_KB * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t operator""_GB(unsigned long long int x) {
|
|
||||||
return 1024_MB * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// useful macros
|
// useful macros
|
||||||
// likely/unlikely
|
// likely/unlikely
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define likely(x) __builtin_expect((x),1)
|
#define likely(x) __builtin_expect((x), 1)
|
||||||
#define unlikely(x) __builtin_expect((x),0)
|
#define unlikely(x) __builtin_expect((x), 0)
|
||||||
#else
|
#else
|
||||||
#define likely(x) (x)
|
#define likely(x) (x)
|
||||||
#define unlikely(x) (x)
|
#define unlikely(x) (x)
|
||||||
#endif
|
#endif
|
|
@ -8,6 +8,7 @@
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
#include "surface_cache.hpp"
|
#include "surface_cache.hpp"
|
||||||
#include "textures.hpp"
|
#include "textures.hpp"
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
|
|
||||||
// More circular dependencies!
|
// More circular dependencies!
|
||||||
class GPU;
|
class GPU;
|
||||||
|
@ -60,11 +61,11 @@ class Renderer {
|
||||||
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
||||||
|
|
||||||
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
|
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
|
||||||
ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer
|
PICA::ColorFmt colourBufferFormat; // Format of the colours stored in the colour buffer
|
||||||
|
|
||||||
// Same for the depth/stencil buffer
|
// Same for the depth/stencil buffer
|
||||||
u32 depthBufferLoc;
|
u32 depthBufferLoc;
|
||||||
DepthBuffer::Formats depthBufferFormat;
|
PICA::DepthFmt depthBufferFormat;
|
||||||
|
|
||||||
// Dummy VAO/VBO for blitting the final output
|
// Dummy VAO/VBO for blitting the final output
|
||||||
OpenGL::VertexArray dummyVAO;
|
OpenGL::VertexArray dummyVAO;
|
||||||
|
@ -93,23 +94,19 @@ class Renderer {
|
||||||
void getGraphicsContext(); // Set up graphics context for rendering
|
void getGraphicsContext(); // Set up graphics context for rendering
|
||||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
||||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
||||||
void drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices); // Draw the given vertices
|
void drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices); // Draw the given vertices
|
||||||
|
|
||||||
void setFBSize(u32 width, u32 height) {
|
void setFBSize(u32 width, u32 height) {
|
||||||
fbSize.x() = width;
|
fbSize.x() = width;
|
||||||
fbSize.y() = height;
|
fbSize.y() = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; }
|
void setColourFormat(PICA::ColorFmt format) { colourBufferFormat = format; }
|
||||||
void setColourFormat(u32 format) { colourBufferFormat = static_cast<ColourBuffer::Formats>(format); }
|
void setDepthFormat(PICA::DepthFmt format) {
|
||||||
|
if (format == PICA::DepthFmt::Unknown1) {
|
||||||
void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; }
|
|
||||||
void setDepthFormat(u32 format) {
|
|
||||||
if (format == 1) {
|
|
||||||
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
|
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
|
||||||
}
|
}
|
||||||
|
depthBufferFormat = format;
|
||||||
depthBufferFormat = static_cast<DepthBuffer::Formats>(format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
|
@ -7,18 +8,8 @@ template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
struct ColourBuffer {
|
struct ColourBuffer {
|
||||||
enum class Formats : u32 {
|
|
||||||
RGBA8 = 0,
|
|
||||||
BGR8 = 1,
|
|
||||||
RGB5A1 = 2,
|
|
||||||
RGB565 = 3,
|
|
||||||
RGBA4 = 4,
|
|
||||||
|
|
||||||
Trash1 = 5, Trash2 = 6, Trash3 = 7 // Technically selectable, but their function is unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
Formats format;
|
PICA::ColorFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -30,7 +21,7 @@ struct ColourBuffer {
|
||||||
|
|
||||||
ColourBuffer() : valid(false) {}
|
ColourBuffer() : valid(false) {}
|
||||||
|
|
||||||
ColourBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true)
|
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true)
|
||||||
: location(loc), format(format), size({x, y}), valid(valid) {
|
: location(loc), format(format), size({x, y}), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -78,31 +69,14 @@ struct ColourBuffer {
|
||||||
size.x() == other.size.x() && size.y() == other.size.y();
|
size.x() == other.size.x() && size.y() == other.size.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size occupied by each pixel in bytes
|
|
||||||
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
|
|
||||||
size_t sizePerPixel() {
|
|
||||||
switch (format) {
|
|
||||||
case Formats::BGR8: return 3;
|
|
||||||
case Formats::RGBA8: return 4;
|
|
||||||
default: return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sizeInBytes() {
|
size_t sizeInBytes() {
|
||||||
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
|
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DepthBuffer {
|
struct DepthBuffer {
|
||||||
enum class Formats : u32 {
|
|
||||||
Depth16 = 0,
|
|
||||||
Garbage = 1,
|
|
||||||
Depth24 = 2,
|
|
||||||
Depth24Stencil8 = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
Formats format;
|
PICA::DepthFmt format;
|
||||||
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -113,7 +87,7 @@ struct DepthBuffer {
|
||||||
|
|
||||||
DepthBuffer() : valid(false) {}
|
DepthBuffer() : valid(false) {}
|
||||||
|
|
||||||
DepthBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) :
|
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) :
|
||||||
location(loc), format(format), size({x, y}), valid(valid) {
|
location(loc), format(format), size({x, y}), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -121,10 +95,6 @@ struct DepthBuffer {
|
||||||
range = Interval<u32>(loc, (u32)endLoc);
|
range = Interval<u32>(loc, (u32)endLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasStencil() {
|
|
||||||
return format == Formats::Depth24Stencil8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocate() {
|
void allocate() {
|
||||||
// Create texture for the FBO, setting up filters and the like
|
// Create texture for the FBO, setting up filters and the like
|
||||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||||
|
@ -167,18 +137,7 @@ struct DepthBuffer {
|
||||||
size.x() == other.size.x() && size.y() == other.size.y();
|
size.x() == other.size.x() && size.y() == other.size.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size occupied by each pixel in bytes
|
|
||||||
size_t sizePerPixel() {
|
|
||||||
switch (format) {
|
|
||||||
case Formats::Depth16: return 2;
|
|
||||||
case Formats::Depth24: return 3;
|
|
||||||
case Formats::Depth24Stencil8: return 4;
|
|
||||||
|
|
||||||
default: return 1; // Invalid format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sizeInBytes() {
|
size_t sizeInBytes() {
|
||||||
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
|
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
|
@ -9,28 +10,9 @@ template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
enum class Formats : u32 {
|
|
||||||
RGBA8 = 0,
|
|
||||||
RGB8 = 1,
|
|
||||||
RGBA5551 = 2,
|
|
||||||
RGB565 = 3,
|
|
||||||
RGBA4 = 4,
|
|
||||||
IA8 = 5,
|
|
||||||
RG8 = 6,
|
|
||||||
I8 = 7,
|
|
||||||
A8 = 8,
|
|
||||||
IA4 = 9,
|
|
||||||
I4 = 10,
|
|
||||||
A4 = 11,
|
|
||||||
ETC1 = 12,
|
|
||||||
ETC1A4 = 13,
|
|
||||||
|
|
||||||
Trash1 = 14, Trash2 = 15 // TODO: What are these?
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||||
Formats format;
|
PICA::TextureFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -41,7 +23,7 @@ struct Texture {
|
||||||
|
|
||||||
Texture() : valid(false) {}
|
Texture() : valid(false) {}
|
||||||
|
|
||||||
Texture(u32 loc, Formats format, u32 x, u32 y, u32 config, bool valid = true)
|
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
||||||
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -62,7 +44,7 @@ struct Texture {
|
||||||
void free();
|
void free();
|
||||||
u64 sizeInBytes();
|
u64 sizeInBytes();
|
||||||
|
|
||||||
u32 decodeTexel(u32 u, u32 v, Formats fmt, const void* data);
|
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data);
|
||||||
|
|
||||||
// Get the morton interleave offset of a texel based on its U and V values
|
// Get the morton interleave offset of a texel based on its U and V values
|
||||||
static u32 mortonInterleave(u32 u, u32 v);
|
static u32 mortonInterleave(u32 u, u32 v);
|
||||||
|
@ -70,12 +52,9 @@ struct Texture {
|
||||||
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
||||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||||
|
|
||||||
// Returns the string representation of a texture format
|
|
||||||
static std::string textureFormatToString(Formats fmt);
|
|
||||||
|
|
||||||
// Returns the format of this texture as a string
|
// Returns the format of this texture as a string
|
||||||
std::string formatToString() {
|
std::string formatToString() {
|
||||||
return textureFormatToString(format);
|
return PICA::textureFormatToString(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||||
|
|
|
@ -88,44 +88,45 @@ template <bool indexed>
|
||||||
void GPU::drawArrays() {
|
void GPU::drawArrays() {
|
||||||
// Base address for vertex attributes
|
// Base address for vertex attributes
|
||||||
// The vertex base is always on a quadword boundary because the PICA does weird alignment shit any time possible
|
// The vertex base is always on a quadword boundary because the PICA does weird alignment shit any time possible
|
||||||
const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
const u32 vertexBase = ((regs[PICA::InternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
||||||
const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
const u32 vertexCount = regs[PICA::InternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
||||||
|
|
||||||
// Configures the type of primitive and the number of vertex shader outputs
|
// Configures the type of primitive and the number of vertex shader outputs
|
||||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||||
const u32 primType = Helpers::getBits<8, 2>(primConfig);
|
const PICA::PrimType primType = static_cast<PICA::PrimType>(Helpers::getBits<8, 2>(primConfig));
|
||||||
if (primType != 0 && primType != 1 && primType != 3) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
if (primType == PICA::PrimType::TriangleFan) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
||||||
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||||
|
|
||||||
if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) {
|
if ((primType == PICA::PrimType::TriangleList && vertexCount % 3) ||
|
||||||
|
(primType == PICA::PrimType::TriangleStrip && vertexCount < 3)) {
|
||||||
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the configuration for the index buffer, used only for indexed drawing
|
// Get the configuration for the index buffer, used only for indexed drawing
|
||||||
u32 indexBufferConfig = regs[PICAInternalRegs::IndexBufferConfig];
|
u32 indexBufferConfig = regs[PICA::InternalRegs::IndexBufferConfig];
|
||||||
u32 indexBufferPointer = vertexBase + (indexBufferConfig & 0xfffffff);
|
u32 indexBufferPointer = vertexBase + (indexBufferConfig & 0xfffffff);
|
||||||
bool shortIndex = Helpers::getBit<31>(indexBufferConfig); // Indicates whether vert indices are 16-bit or 8-bit
|
bool shortIndex = Helpers::getBit<31>(indexBufferConfig); // Indicates whether vert indices are 16-bit or 8-bit
|
||||||
|
|
||||||
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
||||||
// TODO: Cache this when the vertex attribute format registers are written to
|
// TODO: Cache this when the vertex attribute format registers are written to
|
||||||
u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32);
|
u64 vertexCfg = u64(regs[PICA::InternalRegs::AttribFormatLow]) | (u64(regs[PICA::InternalRegs::AttribFormatHigh]) << 32);
|
||||||
|
|
||||||
if constexpr (!indexed) {
|
if constexpr (!indexed) {
|
||||||
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
|
u32 offset = regs[PICA::InternalRegs::VertexOffsetReg];
|
||||||
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
||||||
} else {
|
} else {
|
||||||
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
|
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total number of input attributes to shader. Differs between GS and VS. Currently stubbed to the VS one, as we don't have geometry shaders.
|
// Total number of input attributes to shader. Differs between GS and VS. Currently stubbed to the VS one, as we don't have geometry shaders.
|
||||||
const u32 inputAttrCount = (regs[PICAInternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
|
const u32 inputAttrCount = (regs[PICA::InternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
|
||||||
const u64 inputAttrCfg = getVertexShaderInputConfig();
|
const u64 inputAttrCfg = getVertexShaderInputConfig();
|
||||||
|
|
||||||
for (u32 i = 0; i < vertexCount; i++) {
|
for (u32 i = 0; i < vertexCount; i++) {
|
||||||
u32 vertexIndex; // Index of the vertex in the VBO
|
u32 vertexIndex; // Index of the vertex in the VBO
|
||||||
|
|
||||||
if constexpr (!indexed) {
|
if constexpr (!indexed) {
|
||||||
vertexIndex = i + regs[PICAInternalRegs::VertexOffsetReg];
|
vertexIndex = i + regs[PICA::InternalRegs::VertexOffsetReg];
|
||||||
} else {
|
} else {
|
||||||
if (shortIndex) {
|
if (shortIndex) {
|
||||||
auto ptr = getPointerPhys<u16>(indexBufferPointer);
|
auto ptr = getPointerPhys<u16>(indexBufferPointer);
|
||||||
|
@ -235,9 +236,9 @@ void GPU::drawArrays() {
|
||||||
OutputVertex out;
|
OutputVertex out;
|
||||||
|
|
||||||
// Map shader outputs to fixed function properties
|
// Map shader outputs to fixed function properties
|
||||||
const u32 totalShaderOutputs = regs[PICAInternalRegs::ShaderOutputCount] & 7;
|
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
|
||||||
for (int i = 0; i < totalShaderOutputs; i++) {
|
for (int i = 0; i < totalShaderOutputs; i++) {
|
||||||
const u32 config = regs[PICAInternalRegs::ShaderOutmap0 + i];
|
const u32 config = regs[PICA::InternalRegs::ShaderOutmap0 + i];
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++) { // pls unroll
|
for (int j = 0; j < 4; j++) { // pls unroll
|
||||||
const u32 mapping = (config >> (j * 8)) & 0x1F;
|
const u32 mapping = (config >> (j * 8)) & 0x1F;
|
||||||
|
@ -257,17 +258,12 @@ void GPU::drawArrays() {
|
||||||
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
|
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
|
renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
|
||||||
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
|
|
||||||
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
|
|
||||||
};
|
|
||||||
const auto shape = primTypes[primType];
|
|
||||||
renderer.drawVertices(shape, std::span(vertices).first(vertexCount));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vertex GPU::getImmediateModeVertex() {
|
Vertex GPU::getImmediateModeVertex() {
|
||||||
Vertex v;
|
Vertex v;
|
||||||
const int totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
const int totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
||||||
|
|
||||||
// Copy immediate mode attributes to vertex shader unit
|
// Copy immediate mode attributes to vertex shader unit
|
||||||
for (int i = 0; i < totalAttrCount; i++) {
|
for (int i = 0; i < totalAttrCount; i++) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ u32 GPU::readInternalReg(u32 index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
using namespace PICAInternalRegs;
|
using namespace PICA::InternalRegs;
|
||||||
|
|
||||||
if (index > regNum) {
|
if (index > regNum) {
|
||||||
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
|
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
|
||||||
|
@ -68,7 +68,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
|
|
||||||
case ColourBufferFormat: {
|
case ColourBufferFormat: {
|
||||||
u32 format = getBits<16, 3>(value);
|
u32 format = getBits<16, 3>(value);
|
||||||
renderer.setColourFormat(format);
|
renderer.setColourFormat(static_cast<PICA::ColorFmt>(format));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case DepthBufferFormat: {
|
case DepthBufferFormat: {
|
||||||
u32 fmt = value & 0x3;
|
u32 format = value & 0x3;
|
||||||
renderer.setDepthFormat(fmt);
|
renderer.setDepthFormat(static_cast<PICA::DepthFmt>(format));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
if (fixedAttribIndex < 12) [[likely]] {
|
if (fixedAttribIndex < 12) [[likely]] {
|
||||||
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
|
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
|
||||||
} else if (fixedAttribIndex == 15) { // Otherwise if it's 15, we're submitting an immediate mode vertex
|
} else if (fixedAttribIndex == 15) { // Otherwise if it's 15, we're submitting an immediate mode vertex
|
||||||
const uint totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
const uint totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
||||||
if (totalAttrCount <= immediateModeAttrIndex) {
|
if (totalAttrCount <= immediateModeAttrIndex) {
|
||||||
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
|
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
|
||||||
immediateModeAttrIndex = 0;
|
immediateModeAttrIndex = 0;
|
||||||
|
@ -151,13 +151,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
immediateModeVertices[immediateModeVertIndex++] = v;
|
immediateModeVertices[immediateModeVertIndex++] = v;
|
||||||
|
|
||||||
// Get primitive type
|
// Get primitive type
|
||||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||||
const u32 primType = getBits<8, 2>(primConfig);
|
const u32 primType = getBits<8, 2>(primConfig);
|
||||||
|
|
||||||
// If we've reached 3 verts, issue a draw call
|
// If we've reached 3 verts, issue a draw call
|
||||||
// Handle rendering depending on the primitive type
|
// Handle rendering depending on the primitive type
|
||||||
if (immediateModeVertIndex == 3) {
|
if (immediateModeVertIndex == 3) {
|
||||||
renderer.drawVertices(OpenGL::Triangle, immediateModeVertices);
|
renderer.drawVertices(PICA::PrimType::TriangleList, immediateModeVertices);
|
||||||
|
|
||||||
switch (primType) {
|
switch (primType) {
|
||||||
// Triangle or geometry primitive. Draw a triangle and discard all vertices
|
// Triangle or geometry primitive. Draw a triangle and discard all vertices
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <bit>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
|
|
@ -320,10 +320,10 @@ void Renderer::reset() {
|
||||||
|
|
||||||
// Init the colour/depth buffer settings to some random defaults on reset
|
// Init the colour/depth buffer settings to some random defaults on reset
|
||||||
colourBufferLoc = 0;
|
colourBufferLoc = 0;
|
||||||
colourBufferFormat = ColourBuffer::Formats::RGBA8;
|
colourBufferFormat = PICA::ColorFmt::RGBA8;
|
||||||
|
|
||||||
depthBufferLoc = 0;
|
depthBufferLoc = 0;
|
||||||
depthBufferFormat = DepthBuffer::Formats::Depth16;
|
depthBufferFormat = PICA::DepthFmt::Depth16;
|
||||||
|
|
||||||
if (triangleProgram.exists()) {
|
if (triangleProgram.exists()) {
|
||||||
const auto oldProgram = OpenGL::getProgram();
|
const auto oldProgram = OpenGL::getProgram();
|
||||||
|
@ -436,7 +436,7 @@ void Renderer::initGraphicsContext() {
|
||||||
|
|
||||||
// Set up the OpenGL blending context to match the emulated PICA
|
// Set up the OpenGL blending context to match the emulated PICA
|
||||||
void Renderer::setupBlending() {
|
void Renderer::setupBlending() {
|
||||||
const bool blendingEnabled = (regs[PICAInternalRegs::ColourOperation] & (1 << 8)) != 0;
|
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||||
|
|
||||||
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
|
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
|
||||||
static constexpr std::array<GLenum, 8> blendingEquations = {
|
static constexpr std::array<GLenum, 8> blendingEquations = {
|
||||||
|
@ -456,7 +456,7 @@ void Renderer::setupBlending() {
|
||||||
OpenGL::enableBlend();
|
OpenGL::enableBlend();
|
||||||
|
|
||||||
// Get blending equations
|
// Get blending equations
|
||||||
const u32 blendControl = regs[PICAInternalRegs::BlendFunc];
|
const u32 blendControl = regs[PICA::InternalRegs::BlendFunc];
|
||||||
const u32 rgbEquation = blendControl & 0x7;
|
const u32 rgbEquation = blendControl & 0x7;
|
||||||
const u32 alphaEquation = getBits<8, 3>(blendControl);
|
const u32 alphaEquation = getBits<8, 3>(blendControl);
|
||||||
|
|
||||||
|
@ -466,7 +466,7 @@ void Renderer::setupBlending() {
|
||||||
const u32 alphaSourceFunc = getBits<24, 4>(blendControl);
|
const u32 alphaSourceFunc = getBits<24, 4>(blendControl);
|
||||||
const u32 alphaDestFunc = getBits<28, 4>(blendControl);
|
const u32 alphaDestFunc = getBits<28, 4>(blendControl);
|
||||||
|
|
||||||
const u32 constantColor = regs[PICAInternalRegs::BlendColour];
|
const u32 constantColor = regs[PICA::InternalRegs::BlendColour];
|
||||||
const u32 r = constantColor & 0xff;
|
const u32 r = constantColor & 0xff;
|
||||||
const u32 g = getBits<8, 8>(constantColor);
|
const u32 g = getBits<8, 8>(constantColor);
|
||||||
const u32 b = getBits<16, 8>(constantColor);
|
const u32 b = getBits<16, 8>(constantColor);
|
||||||
|
@ -511,7 +511,13 @@ void Renderer::setupTextureEnvState() {
|
||||||
glUniform1ui(textureEnvBufferColorLoc, regs[0xfd]);
|
glUniform1ui(textureEnvBufferColorLoc, regs[0xfd]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices) {
|
void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) {
|
||||||
|
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
|
||||||
|
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
|
||||||
|
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
|
||||||
|
};
|
||||||
|
const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
|
||||||
|
|
||||||
// TODO: We should implement a GL state tracker that tracks settings like scissor, blending, bound program, etc
|
// TODO: We should implement a GL state tracker that tracks settings like scissor, blending, bound program, etc
|
||||||
// This way if we attempt to eg do multiple glEnable(GL_BLEND) calls in a row, it will say "Oh blending is already enabled"
|
// This way if we attempt to eg do multiple glEnable(GL_BLEND) calls in a row, it will say "Oh blending is already enabled"
|
||||||
// And not actually perform the very expensive driver call for it
|
// And not actually perform the very expensive driver call for it
|
||||||
|
@ -522,7 +528,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
triangleProgram.use();
|
triangleProgram.use();
|
||||||
|
|
||||||
// Adjust alpha test if necessary
|
// Adjust alpha test if necessary
|
||||||
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
|
const u32 alphaControl = regs[PICA::InternalRegs::AlphaTestConfig];
|
||||||
if (alphaControl != oldAlphaControl) {
|
if (alphaControl != oldAlphaControl) {
|
||||||
oldAlphaControl = alphaControl;
|
oldAlphaControl = alphaControl;
|
||||||
glUniform1ui(alphaControlLoc, alphaControl);
|
glUniform1ui(alphaControlLoc, alphaControl);
|
||||||
|
@ -532,7 +538,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
OpenGL::Framebuffer poop = getColourFBO();
|
OpenGL::Framebuffer poop = getColourFBO();
|
||||||
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
||||||
|
|
||||||
const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask];
|
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
|
||||||
const bool depthEnable = depthControl & 1;
|
const bool depthEnable = depthControl & 1;
|
||||||
const bool depthWriteEnable = getBit<12>(depthControl);
|
const bool depthWriteEnable = getBit<12>(depthControl);
|
||||||
const int depthFunc = getBits<4, 3>(depthControl);
|
const int depthFunc = getBits<4, 3>(depthControl);
|
||||||
|
@ -543,9 +549,9 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
|
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
|
||||||
};
|
};
|
||||||
|
|
||||||
const float depthScale = f24::fromRaw(regs[PICAInternalRegs::DepthScale] & 0xffffff).toFloat32();
|
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
|
||||||
const float depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||||
const bool depthMapEnable = regs[PICAInternalRegs::DepthmapEnable] & 1;
|
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
|
||||||
|
|
||||||
// Update depth uniforms
|
// Update depth uniforms
|
||||||
if (oldDepthScale != depthScale) {
|
if (oldDepthScale != depthScale) {
|
||||||
|
@ -585,7 +591,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
u32 format = regs[ioBase + (i == 0 ? 14 : 6)] & 0xF;
|
u32 format = regs[ioBase + (i == 0 ? 14 : 6)] & 0xF;
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + i);
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
Texture targetTex(addr, static_cast<Texture::Formats>(format), width, height, config);
|
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||||
OpenGL::Texture tex = getTexture(targetTex);
|
OpenGL::Texture tex = getTexture(targetTex);
|
||||||
tex.bind();
|
tex.bind();
|
||||||
}
|
}
|
||||||
|
@ -593,15 +599,15 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
// Update the texture unit configuration uniform if it changed
|
// Update the texture unit configuration uniform if it changed
|
||||||
const u32 texUnitConfig = regs[PICAInternalRegs::TexUnitCfg];
|
const u32 texUnitConfig = regs[PICA::InternalRegs::TexUnitCfg];
|
||||||
if (oldTexUnitConfig != texUnitConfig) {
|
if (oldTexUnitConfig != texUnitConfig) {
|
||||||
oldTexUnitConfig = texUnitConfig;
|
oldTexUnitConfig = texUnitConfig;
|
||||||
glUniform1ui(texUnitConfigLoc, texUnitConfig);
|
glUniform1ui(texUnitConfigLoc, texUnitConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Actually use this
|
// TODO: Actually use this
|
||||||
float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
float viewportWidth = f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
||||||
float viewportHeight = f24::fromRaw(regs[PICAInternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
float viewportHeight = f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
||||||
OpenGL::setViewport(viewportWidth, viewportHeight);
|
OpenGL::setViewport(viewportWidth, viewportHeight);
|
||||||
|
|
||||||
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
|
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
|
||||||
|
@ -623,7 +629,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
||||||
}
|
}
|
||||||
|
|
||||||
vbo.bufferVertsSub(vertices);
|
vbo.bufferVertsSub(vertices);
|
||||||
OpenGL::draw(primType, vertices.size());
|
OpenGL::draw(primitiveTopology, vertices.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 topScreenBuffer = 0x1f000000;
|
constexpr u32 topScreenBuffer = 0x1f000000;
|
||||||
|
@ -685,8 +691,8 @@ void Renderer::bindDepthBuffer() {
|
||||||
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
|
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DepthBuffer::Formats::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
if (PICA::DepthFmt::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
||||||
auto attachment = depthBufferFormat == DepthBuffer::Formats::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
auto attachment = depthBufferFormat == PICA::DepthFmt::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,34 +43,34 @@ u64 Texture::sizeInBytes() {
|
||||||
u64 pixelCount = u64(size.x()) * u64(size.y());
|
u64 pixelCount = u64(size.x()) * u64(size.y());
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case Formats::RGBA8: // 4 bytes per pixel
|
case PICA::TextureFmt::RGBA8: // 4 bytes per pixel
|
||||||
return pixelCount * 4;
|
return pixelCount * 4;
|
||||||
|
|
||||||
case Formats::RGB8: // 3 bytes per pixel
|
case PICA::TextureFmt::RGB8: // 3 bytes per pixel
|
||||||
return pixelCount * 3;
|
return pixelCount * 3;
|
||||||
|
|
||||||
case Formats::RGBA5551: // 2 bytes per pixel
|
case PICA::TextureFmt::RGBA5551: // 2 bytes per pixel
|
||||||
case Formats::RGB565:
|
case PICA::TextureFmt::RGB565:
|
||||||
case Formats::RGBA4:
|
case PICA::TextureFmt::RGBA4:
|
||||||
case Formats::RG8:
|
case PICA::TextureFmt::RG8:
|
||||||
case Formats::IA8:
|
case PICA::TextureFmt::IA8:
|
||||||
return pixelCount * 2;
|
return pixelCount * 2;
|
||||||
|
|
||||||
case Formats::A8: // 1 byte per pixel
|
case PICA::TextureFmt::A8: // 1 byte per pixel
|
||||||
case Formats::I8:
|
case PICA::TextureFmt::I8:
|
||||||
case Formats::IA4:
|
case PICA::TextureFmt::IA4:
|
||||||
return pixelCount;
|
return pixelCount;
|
||||||
|
|
||||||
case Formats::I4: // 4 bits per pixel
|
case PICA::TextureFmt::I4: // 4 bits per pixel
|
||||||
case Formats::A4:
|
case PICA::TextureFmt::A4:
|
||||||
return pixelCount / 2;
|
return pixelCount / 2;
|
||||||
|
|
||||||
case Formats::ETC1: // Compressed formats
|
case PICA::TextureFmt::ETC1: // Compressed formats
|
||||||
case Formats::ETC1A4: {
|
case PICA::TextureFmt::ETC1A4: {
|
||||||
// Number of 4x4 tiles
|
// Number of 4x4 tiles
|
||||||
const u64 tileCount = pixelCount / 16;
|
const u64 tileCount = pixelCount / 16;
|
||||||
// Tiles are 8 bytes each on ETC1 and 16 bytes each on ETC1A4
|
// Tiles are 8 bytes each on ETC1 and 16 bytes each on ETC1A4
|
||||||
const u64 tileSize = format == Formats::ETC1 ? 8 : 16;
|
const u64 tileSize = format == PICA::TextureFmt::ETC1 ? 8 : 16;
|
||||||
return tileCount * tileSize;
|
return tileCount * tileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,9 +111,9 @@ u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
|
||||||
// Get the texel at position (u, v)
|
// Get the texel at position (u, v)
|
||||||
// fmt: format of the texture
|
// fmt: format of the texture
|
||||||
// data: texture data of the texture
|
// data: texture data of the texture
|
||||||
u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) {
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case Formats::RGBA4: {
|
case PICA::TextureFmt::RGBA4: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -126,7 +126,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGBA5551: {
|
case PICA::TextureFmt::RGBA5551: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -139,7 +139,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGB565: {
|
case PICA::TextureFmt::RGB565: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -151,7 +151,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RG8: {
|
case PICA::TextureFmt::RG8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGB8: {
|
case PICA::TextureFmt::RGB8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGBA8: {
|
case PICA::TextureFmt::RGBA8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::IA4: {
|
case PICA::TextureFmt::IA4: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::A4: {
|
case PICA::TextureFmt::A4: {
|
||||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::A8: {
|
case PICA::TextureFmt::A8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
const u8 alpha = ptr[offset];
|
const u8 alpha = ptr[offset];
|
||||||
|
@ -218,7 +218,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::I4: {
|
case PICA::TextureFmt::I4: {
|
||||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::I8: {
|
case PICA::TextureFmt::I8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
const u8 intensity = ptr[offset];
|
const u8 intensity = ptr[offset];
|
||||||
|
@ -239,7 +239,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::IA8: {
|
case PICA::TextureFmt::IA8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -249,8 +249,8 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
||||||
case Formats::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
|
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
|
||||||
|
@ -272,23 +272,3 @@ void Texture::decodeTexture(const void* data) {
|
||||||
texture.bind();
|
texture.bind();
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.u(), size.v(), GL_RGBA, GL_UNSIGNED_BYTE, decoded.data());
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.u(), size.v(), GL_RGBA, GL_UNSIGNED_BYTE, decoded.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Texture::textureFormatToString(Texture::Formats fmt) {
|
|
||||||
switch (fmt) {
|
|
||||||
case Formats::A4: return "A4";
|
|
||||||
case Formats::A8: return "A8";
|
|
||||||
case Formats::ETC1: return "ETC1";
|
|
||||||
case Formats::ETC1A4: return "ETC1A4";
|
|
||||||
case Formats::I4: return "I4";
|
|
||||||
case Formats::I8: return "I8";
|
|
||||||
case Formats::IA4: return "IA4";
|
|
||||||
case Formats::IA8: return "IA8";
|
|
||||||
case Formats::RG8: return "RG8";
|
|
||||||
case Formats::RGB565: return "RGB565";
|
|
||||||
case Formats::RGB8: return "RGB8";
|
|
||||||
case Formats::RGBA4: return "RGBA4";
|
|
||||||
case Formats::RGBA5551: return "RGBA5551";
|
|
||||||
case Formats::RGBA8: return "RGBA8";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -73,7 +73,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
mem.write32(output + i * 4, std::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
|
mem.write32(output + i * 4, Helpers::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
|
||||||
}
|
}
|
||||||
} else if (size == 0x1C && blockID == 0xA0000) { // Username
|
} else if (size == 0x1C && blockID == 0xA0000) { // Username
|
||||||
writeStringU16(output, u"Pander");
|
writeStringU16(output, u"Pander");
|
||||||
|
|
|
@ -83,7 +83,7 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
||||||
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, std::bit_cast<u32, float>(gyroscopeCoeff));
|
mem.write32(messagePointer + 8, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIDService::getIPCHandles(u32 messagePointer) {
|
void HIDService::getIPCHandles(u32 messagePointer) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue